Files changed (1) hide show
  1. app.py +28 -14
app.py CHANGED
@@ -29,14 +29,14 @@ SAFE_IMG_SIZE = 512
29
  SOURCE_AGE = 20
30
  TARGET_AGE = 80
31
  WRINKLE_STRENGTH = 0.42
32
- CONTRAST_BOOST = 1.10
33
- SHARPNESS_BOOST = 1.20
34
- ALPHA_HAIR = 0.95
35
  BLUR_RADIUS = 7
36
  EDGE_SMOOTHING = True
37
  USE_GFPGAN = True
38
  GFPGAN_UPSCALE = 1
39
- GFPGAN_WEIGHT = 0.5
40
 
41
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
42
  print(f"🚀 Device: {DEVICE}")
@@ -88,11 +88,10 @@ def load_gfpgan():
88
  channel_multiplier=2,
89
  bg_upsampler=None,
90
  device=DEVICE,
91
- half=False # Safe setting for HF Space
92
  )
93
  print("✅ GFPGAN loaded successfully with FP32!")
94
  return gfpgan_restorer
95
-
96
  except Exception as e:
97
  print(f"❌ GFPGAN load failed: {e}")
98
  return None
@@ -104,6 +103,7 @@ def load_aging_model():
104
  return age_model
105
 
106
  print("Loading UNet aging model...")
 
107
  class DownLayer(nn.Module):
108
  def __init__(self, in_ch, out_ch):
109
  super().__init__()
@@ -239,6 +239,7 @@ def get_lips_mask(pil_image: Image.Image) -> np.ndarray:
239
  return lips_mask
240
  return np.zeros((h, w), dtype=np.float32)
241
 
 
242
  def exclude_lips_from_mask(beard_mask: np.ndarray, pil_image: Image.Image) -> np.ndarray:
243
  if np.sum(beard_mask) == 0:
244
  return beard_mask
@@ -250,6 +251,7 @@ def exclude_lips_from_mask(beard_mask: np.ndarray, pil_image: Image.Image) -> np
250
  beard_mask = cv2.GaussianBlur(beard_mask, (5, 5), 1)
251
  return beard_mask
252
 
 
253
  def get_beard_mask(pil_image: Image.Image) -> np.ndarray:
254
  temp_path = "temp_input.jpg"
255
  try:
@@ -281,6 +283,7 @@ def get_beard_mask(pil_image: Image.Image) -> np.ndarray:
281
  if os.path.exists(temp_path):
282
  os.remove(temp_path)
283
 
 
284
  def clean_mask(mask, min_area=150):
285
  mask = mask.astype(np.uint8)
286
  labeled, num = label(mask)
@@ -290,6 +293,7 @@ def clean_mask(mask, min_area=150):
290
  new_mask[labeled == i] = 1
291
  return new_mask
292
 
 
293
  def get_hair_mask_segformer(pil_image: Image.Image) -> np.ndarray:
294
  processor, parser = load_face_parser()
295
  inputs = processor(images=pil_image, return_tensors="pt").to(DEVICE)
@@ -313,6 +317,7 @@ def get_hair_mask_segformer(pil_image: Image.Image) -> np.ndarray:
313
  hair_mask = np.clip(hair_mask, 0, 1)
314
  return hair_mask
315
 
 
316
  def apply_hair_and_beard_color(image: Image.Image, hair_mask: np.ndarray, beard_mask: np.ndarray):
317
  combined_mask = np.maximum(hair_mask, beard_mask)
318
  if np.sum(combined_mask) == 0:
@@ -332,24 +337,27 @@ def apply_hair_and_beard_color(image: Image.Image, hair_mask: np.ndarray, beard_
332
  result = (1 - alpha * combined_mask[..., np.newaxis]) * img_np + (alpha * combined_mask[..., np.newaxis]) * white_layer
333
  result = np.clip(result, 0, 255).astype(np.uint8)
334
  result_pil = Image.fromarray(result)
335
- result_pil = result_pil.filter(ImageFilter.UnsharpMask(1.2, 140, 2))
336
  return result_pil
337
 
 
338
  def post_correct_aged(original: Image.Image, aged: Image.Image) -> Image.Image:
339
  orig_np = np.array(original)
340
  aged_np = np.array(aged)
341
  matched = match_histograms(aged_np, orig_np, channel_axis=-1)
342
  matched_img = Image.fromarray(np.clip(matched, 0, 255).astype(np.uint8))
343
- matched_img = ImageEnhance.Brightness(matched_img).enhance(1.10)
344
- matched_img = ImageEnhance.Contrast(matched_img).enhance(1.06)
345
  return matched_img
346
 
 
347
  def enhance_texture(img: Image.Image) -> Image.Image:
348
- img = img.filter(ImageFilter.UnsharpMask(2, 160, 3))
349
  img = ImageEnhance.Contrast(img).enhance(CONTRAST_BOOST)
350
  img = ImageEnhance.Sharpness(img).enhance(SHARPNESS_BOOST)
351
  return img
352
 
 
353
  # ================== MAIN PROCESSING FUNCTION ==================
354
  def process_face_aging(input_image: Image.Image) -> Image.Image:
355
  if input_image is None:
@@ -374,8 +382,9 @@ def process_face_aging(input_image: Image.Image) -> Image.Image:
374
  blended = (1 - alpha) * rgb_tensor.unsqueeze(0) + alpha * raw_output
375
  blended = blended.clamp(0, 1).squeeze(0)
376
  final_aged = TF.to_pil_image(blended).resize((ow, oh), Image.LANCZOS)
377
- final_aged = enhance_texture(final_aged)
378
- final_aged = post_correct_aged(orig, final_aged)
 
379
 
380
  print(" Generating hair mask...")
381
  hair_mask = get_hair_mask_segformer(final_aged)
@@ -394,8 +403,11 @@ def process_face_aging(input_image: Image.Image) -> Image.Image:
394
  img_cv = cv2.cvtColor(np.array(final_img), cv2.COLOR_RGB2BGR)
395
  with torch.cuda.amp.autocast(enabled=(DEVICE.type == "cuda")):
396
  _, _, restored_cv = gfpgan.enhance(
397
- img_cv, has_aligned=False, only_center_face=False,
398
- paste_back=True, weight=GFPGAN_WEIGHT
 
 
 
399
  )
400
  final_img = Image.fromarray(cv2.cvtColor(restored_cv, cv2.COLOR_BGR2RGB))
401
  except Exception as e:
@@ -412,6 +424,7 @@ def process_face_aging(input_image: Image.Image) -> Image.Image:
412
  traceback.print_exc()
413
  raise
414
 
 
415
  # ================== FASTAPI SETUP ==================
416
  app = FastAPI(title="Face Aging + White Hair & Beard API")
417
 
@@ -448,6 +461,7 @@ async def age_face(file: UploadFile = File(...)):
448
  if DEVICE.type == "cuda":
449
  torch.cuda.empty_cache()
450
 
 
451
  # For local testing
452
  if __name__ == "__main__":
453
  import uvicorn
 
29
  SOURCE_AGE = 20
30
  TARGET_AGE = 80
31
  WRINKLE_STRENGTH = 0.42
32
+ CONTRAST_BOOST = 1.15 # thoda badhaya
33
+ SHARPNESS_BOOST = 1.35 # thoda badhaya
34
+ ALPHA_HAIR = 0.92
35
  BLUR_RADIUS = 7
36
  EDGE_SMOOTHING = True
37
  USE_GFPGAN = True
38
  GFPGAN_UPSCALE = 1
39
+ GFPGAN_WEIGHT = 0.75 # ← Quality ke liye badhaya (0.5 se 0.75)
40
 
41
  DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
42
  print(f"🚀 Device: {DEVICE}")
 
88
  channel_multiplier=2,
89
  bg_upsampler=None,
90
  device=DEVICE,
91
+ half=False
92
  )
93
  print("✅ GFPGAN loaded successfully with FP32!")
94
  return gfpgan_restorer
 
95
  except Exception as e:
96
  print(f"❌ GFPGAN load failed: {e}")
97
  return None
 
103
  return age_model
104
 
105
  print("Loading UNet aging model...")
106
+
107
  class DownLayer(nn.Module):
108
  def __init__(self, in_ch, out_ch):
109
  super().__init__()
 
239
  return lips_mask
240
  return np.zeros((h, w), dtype=np.float32)
241
 
242
+
243
  def exclude_lips_from_mask(beard_mask: np.ndarray, pil_image: Image.Image) -> np.ndarray:
244
  if np.sum(beard_mask) == 0:
245
  return beard_mask
 
251
  beard_mask = cv2.GaussianBlur(beard_mask, (5, 5), 1)
252
  return beard_mask
253
 
254
+
255
  def get_beard_mask(pil_image: Image.Image) -> np.ndarray:
256
  temp_path = "temp_input.jpg"
257
  try:
 
283
  if os.path.exists(temp_path):
284
  os.remove(temp_path)
285
 
286
+
287
  def clean_mask(mask, min_area=150):
288
  mask = mask.astype(np.uint8)
289
  labeled, num = label(mask)
 
293
  new_mask[labeled == i] = 1
294
  return new_mask
295
 
296
+
297
  def get_hair_mask_segformer(pil_image: Image.Image) -> np.ndarray:
298
  processor, parser = load_face_parser()
299
  inputs = processor(images=pil_image, return_tensors="pt").to(DEVICE)
 
317
  hair_mask = np.clip(hair_mask, 0, 1)
318
  return hair_mask
319
 
320
+
321
  def apply_hair_and_beard_color(image: Image.Image, hair_mask: np.ndarray, beard_mask: np.ndarray):
322
  combined_mask = np.maximum(hair_mask, beard_mask)
323
  if np.sum(combined_mask) == 0:
 
337
  result = (1 - alpha * combined_mask[..., np.newaxis]) * img_np + (alpha * combined_mask[..., np.newaxis]) * white_layer
338
  result = np.clip(result, 0, 255).astype(np.uint8)
339
  result_pil = Image.fromarray(result)
340
+ result_pil = result_pil.filter(ImageFilter.UnsharpMask(1.3, 150, 2))
341
  return result_pil
342
 
343
+
344
  def post_correct_aged(original: Image.Image, aged: Image.Image) -> Image.Image:
345
  orig_np = np.array(original)
346
  aged_np = np.array(aged)
347
  matched = match_histograms(aged_np, orig_np, channel_axis=-1)
348
  matched_img = Image.fromarray(np.clip(matched, 0, 255).astype(np.uint8))
349
+ matched_img = ImageEnhance.Brightness(matched_img).enhance(1.12)
350
+ matched_img = ImageEnhance.Contrast(matched_img).enhance(1.18)
351
  return matched_img
352
 
353
+
354
  def enhance_texture(img: Image.Image) -> Image.Image:
355
+ img = img.filter(ImageFilter.UnsharpMask(radius=2.5, percent=180, threshold=3)) # Stronger sharpening
356
  img = ImageEnhance.Contrast(img).enhance(CONTRAST_BOOST)
357
  img = ImageEnhance.Sharpness(img).enhance(SHARPNESS_BOOST)
358
  return img
359
 
360
+
361
  # ================== MAIN PROCESSING FUNCTION ==================
362
  def process_face_aging(input_image: Image.Image) -> Image.Image:
363
  if input_image is None:
 
382
  blended = (1 - alpha) * rgb_tensor.unsqueeze(0) + alpha * raw_output
383
  blended = blended.clamp(0, 1).squeeze(0)
384
  final_aged = TF.to_pil_image(blended).resize((ow, oh), Image.LANCZOS)
385
+
386
+ final_aged = enhance_texture(final_aged)
387
+ final_aged = post_correct_aged(orig, final_aged)
388
 
389
  print(" Generating hair mask...")
390
  hair_mask = get_hair_mask_segformer(final_aged)
 
403
  img_cv = cv2.cvtColor(np.array(final_img), cv2.COLOR_RGB2BGR)
404
  with torch.cuda.amp.autocast(enabled=(DEVICE.type == "cuda")):
405
  _, _, restored_cv = gfpgan.enhance(
406
+ img_cv,
407
+ has_aligned=False,
408
+ only_center_face=False,
409
+ paste_back=True,
410
+ weight=GFPGAN_WEIGHT
411
  )
412
  final_img = Image.fromarray(cv2.cvtColor(restored_cv, cv2.COLOR_BGR2RGB))
413
  except Exception as e:
 
424
  traceback.print_exc()
425
  raise
426
 
427
+
428
  # ================== FASTAPI SETUP ==================
429
  app = FastAPI(title="Face Aging + White Hair & Beard API")
430
 
 
461
  if DEVICE.type == "cuda":
462
  torch.cuda.empty_cache()
463
 
464
+
465
  # For local testing
466
  if __name__ == "__main__":
467
  import uvicorn