Incostra commited on
Commit
2836940
Β·
verified Β·
1 Parent(s): f98a7ef

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -25
app.py CHANGED
@@ -4,27 +4,50 @@ import numpy as np
4
  import math
5
  import json
6
  from PIL import Image
7
-
8
- # ── MediaPipe setup ──
9
- mp_face_mesh = mp.solutions.face_mesh
10
- face_mesh = mp_face_mesh.FaceMesh(
11
- static_image_mode=True,
12
- max_num_faces=1,
13
- refine_landmarks=True,
14
- min_detection_confidence=0.4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  )
 
 
 
16
 
17
  def analyse_face(image):
18
  try:
19
- img_rgb = np.array(image.convert("RGB"))
20
- h, w = img_rgb.shape[:2]
 
 
21
 
22
- results = face_mesh.process(img_rgb)
23
 
24
- if not results.multi_face_landmarks:
25
  return json.dumps({"error": "No face detected. Please upload a clear, front-facing photo."})
26
 
27
- lm = results.multi_face_landmarks[0].landmark
28
 
29
  def dist(a, b):
30
  return math.sqrt(
@@ -32,12 +55,11 @@ def analyse_face(image):
32
  ((lm[a].y - lm[b].y) * h) ** 2
33
  )
34
 
35
- # Reference distance
36
  face_ref = dist(33, 263)
37
  if face_ref == 0:
38
  return json.dumps({"error": "Could not measure face. Please try a different photo."})
39
 
40
- # Age estimation
41
  face_height = dist(10, 152)
42
  lower_face_h = dist(168, 152)
43
  lower_ratio = lower_face_h / face_height if face_height > 0 else 0.52
@@ -49,10 +71,10 @@ def analyse_face(image):
49
  nasolabial = ((dist(50, 61) + dist(280, 291)) / 2) / face_ref
50
 
51
  forehead_idx = [10,109,67,103,54,21,162,127,234,338,297,332,284,251,389,356,454]
52
- z_vals = [lm[i].z for i in forehead_idx]
53
- z_mean = sum(z_vals) / len(z_vals)
54
- z_var = sum((z - z_mean) ** 2 for z in z_vals) / len(z_vals)
55
- texture = math.sqrt(abs(z_var)) * 100
56
 
57
  age_raw = (20
58
  + (lower_ratio - 0.52) * 120
@@ -63,14 +85,14 @@ def analyse_face(image):
63
  age_mid = max(18, min(72, round(age_raw)))
64
  age_range = f"{max(18, age_mid - 4)}\u2013{age_mid + 4}"
65
 
66
- # Wrinkle score
67
  wrinkle = round(max(1.0, min(9.9, 1 + texture * 18 + (age_mid - 18) * 0.10)), 1)
68
 
69
- # Elasticity score
70
- cheek_sag = ((dist(116, 61) + dist(345, 291)) / 2) / face_ref
71
  elasticity = round(max(1.0, min(9.9, 10 - (cheek_sag - 0.55) * 18 - (age_mid - 18) * 0.10)), 1)
72
 
73
- # Jawline score
74
  jaw_pts = [234,93,132,58,172,136,150,149,176,148,152,377,400,378,379,365,397,288,361,323,454]
75
  jaw_dev = 0.0
76
  for i in range(1, len(jaw_pts) - 1):
@@ -83,7 +105,7 @@ def analyse_face(image):
83
  jaw_dev /= len(jaw_pts)
84
  jawline = round(max(1.0, min(9.9, 10 - jaw_dev * 0.8 - (age_mid - 18) * 0.09)), 1)
85
 
86
- age_factor = round(max(0, min(1, (age_mid - 18) / 54)), 3)
87
  years_younger = max(3, round(age_factor * 14 + 2))
88
 
89
  landmarks_out = [{"x": float(l.x), "y": float(l.y), "z": float(l.z)} for l in lm]
@@ -102,7 +124,8 @@ def analyse_face(image):
102
  })
103
 
104
  except Exception as e:
105
- return json.dumps({"error": str(e)})
 
106
 
107
 
108
  iface = gr.Interface(
 
4
  import math
5
  import json
6
  from PIL import Image
7
+ from mediapipe.tasks import python as mp_python
8
+ from mediapipe.tasks.python import vision as mp_vision
9
+ import urllib.request
10
+ import os
11
+
12
+ # ── Download model if not cached ──
13
+ MODEL_PATH = "/tmp/face_landmarker.task"
14
+ MODEL_URL = "https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task"
15
+
16
+ if not os.path.exists(MODEL_PATH):
17
+ print("Downloading face landmarker model...")
18
+ urllib.request.urlretrieve(MODEL_URL, MODEL_PATH)
19
+ print("Model downloaded.")
20
+
21
+ # ── Build FaceLandmarker ──
22
+ base_options = mp_python.BaseOptions(
23
+ model_asset_path=MODEL_PATH,
24
+ delegate=mp_python.BaseOptions.Delegate.CPU
25
+ )
26
+ options = mp_vision.FaceLandmarkerOptions(
27
+ base_options=base_options,
28
+ output_face_blendshapes=False,
29
+ output_facial_transformation_matrixes=False,
30
+ num_faces=1,
31
+ min_face_detection_confidence=0.4,
32
+ min_face_presence_confidence=0.4
33
  )
34
+ landmarker = mp_vision.FaceLandmarker.create_from_options(options)
35
+ print("FaceLandmarker ready.")
36
+
37
 
38
  def analyse_face(image):
39
  try:
40
+ # Convert PIL to MediaPipe Image
41
+ img_rgb = np.array(image.convert("RGB"))
42
+ h, w = img_rgb.shape[:2]
43
+ mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=img_rgb)
44
 
45
+ result = landmarker.detect(mp_image)
46
 
47
+ if not result.face_landmarks or len(result.face_landmarks) == 0:
48
  return json.dumps({"error": "No face detected. Please upload a clear, front-facing photo."})
49
 
50
+ lm = result.face_landmarks[0] # list of NormalizedLandmark
51
 
52
  def dist(a, b):
53
  return math.sqrt(
 
55
  ((lm[a].y - lm[b].y) * h) ** 2
56
  )
57
 
 
58
  face_ref = dist(33, 263)
59
  if face_ref == 0:
60
  return json.dumps({"error": "Could not measure face. Please try a different photo."})
61
 
62
+ # ── Age estimation ──
63
  face_height = dist(10, 152)
64
  lower_face_h = dist(168, 152)
65
  lower_ratio = lower_face_h / face_height if face_height > 0 else 0.52
 
71
  nasolabial = ((dist(50, 61) + dist(280, 291)) / 2) / face_ref
72
 
73
  forehead_idx = [10,109,67,103,54,21,162,127,234,338,297,332,284,251,389,356,454]
74
+ z_vals = [lm[i].z for i in forehead_idx]
75
+ z_mean = sum(z_vals) / len(z_vals)
76
+ z_var = sum((z - z_mean) ** 2 for z in z_vals) / len(z_vals)
77
+ texture = math.sqrt(abs(z_var)) * 100
78
 
79
  age_raw = (20
80
  + (lower_ratio - 0.52) * 120
 
85
  age_mid = max(18, min(72, round(age_raw)))
86
  age_range = f"{max(18, age_mid - 4)}\u2013{age_mid + 4}"
87
 
88
+ # ── Wrinkle score ──
89
  wrinkle = round(max(1.0, min(9.9, 1 + texture * 18 + (age_mid - 18) * 0.10)), 1)
90
 
91
+ # ── Elasticity score ──
92
+ cheek_sag = ((dist(116, 61) + dist(345, 291)) / 2) / face_ref
93
  elasticity = round(max(1.0, min(9.9, 10 - (cheek_sag - 0.55) * 18 - (age_mid - 18) * 0.10)), 1)
94
 
95
+ # ── Jawline score ──
96
  jaw_pts = [234,93,132,58,172,136,150,149,176,148,152,377,400,378,379,365,397,288,361,323,454]
97
  jaw_dev = 0.0
98
  for i in range(1, len(jaw_pts) - 1):
 
105
  jaw_dev /= len(jaw_pts)
106
  jawline = round(max(1.0, min(9.9, 10 - jaw_dev * 0.8 - (age_mid - 18) * 0.09)), 1)
107
 
108
+ age_factor = round(max(0.0, min(1.0, (age_mid - 18) / 54)), 3)
109
  years_younger = max(3, round(age_factor * 14 + 2))
110
 
111
  landmarks_out = [{"x": float(l.x), "y": float(l.y), "z": float(l.z)} for l in lm]
 
124
  })
125
 
126
  except Exception as e:
127
+ import traceback
128
+ return json.dumps({"error": str(e), "trace": traceback.format_exc()})
129
 
130
 
131
  iface = gr.Interface(