anujakkulkarni commited on
Commit
ad5c83b
·
verified ·
1 Parent(s): 4d81a3d

Update skin_analysis.py

Browse files
Files changed (1) hide show
  1. skin_analysis.py +284 -22
skin_analysis.py CHANGED
@@ -89,7 +89,7 @@ def analyze_skin_complete(
89
  max_retries: int = 3
90
  ) -> Optional[Dict[str, Any]]:
91
  """
92
- Complete skin analysis with ONE API call instead of 6 separate calls.
93
 
94
  Args:
95
  image_path: Path to the facial image
@@ -101,7 +101,7 @@ def analyze_skin_complete(
101
  """
102
 
103
  # Check cache first
104
- cache_key = f"complete_{get_image_hash(image_path)}"
105
  if use_cache and cache_key in _analysis_cache:
106
  print("✓ Using cached analysis results")
107
  return _analysis_cache[cache_key]
@@ -115,7 +115,7 @@ def analyze_skin_complete(
115
 
116
  image_part = Part.from_bytes(data=image_bytes, mime_type="image/jpeg")
117
 
118
- # Combined comprehensive prompt
119
  prompt = """
120
  You are an advanced AI skin analysis system. Analyze the face in this image comprehensively.
121
 
@@ -147,6 +147,29 @@ Return STRICT JSON with ALL these fields (use exact field names):
147
  "scarring": float (0.0-1.0, acne scarring),
148
  "congestion": float (0.0-1.0, pore congestion)
149
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  "age_analysis": {
151
  "fitzpatrick_type": integer (1-6, skin type based on melanin),
152
  "eye_age": integer (estimated age of eye area),
@@ -154,6 +177,25 @@ Return STRICT JSON with ALL these fields (use exact field names):
154
  }
155
  }
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  CRITICAL RULES:
158
  - Return ONLY raw JSON, no markdown formatting
159
  - No explanations, no text outside JSON
@@ -162,6 +204,7 @@ CRITICAL RULES:
162
  - Base analysis ONLY on visible features in the image
163
  - Do NOT guess or infer anything not visible
164
  - Ensure all fields are present in the response
 
165
  """
166
 
167
  # Make API call
@@ -234,6 +277,7 @@ def compute_hydration_score(hydration_factors: Dict[str, float]) -> Optional[flo
234
  def compute_pigmentation_score(pigmentation_factors: Dict[str, float]) -> Optional[float]:
235
  """
236
  Calculate pigmentation score (0-100) from factors
 
237
  """
238
  if not pigmentation_factors:
239
  return None
@@ -255,6 +299,7 @@ def compute_pigmentation_score(pigmentation_factors: Dict[str, float]) -> Option
255
  def compute_acne_score(acne_factors: Dict[str, float]) -> Optional[float]:
256
  """
257
  Calculate acne score (0-100) from factors
 
258
  """
259
  if not acne_factors:
260
  return None
@@ -272,9 +317,54 @@ def compute_acne_score(acne_factors: Dict[str, float]) -> Optional[float]:
272
  return None
273
 
274
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  def get_comprehensive_analysis(image_path: str) -> Optional[Dict[str, Any]]:
276
  """
277
- Get complete skin analysis with computed scores.
278
 
279
  Returns a dictionary with all raw factors plus computed scores.
280
  """
@@ -289,7 +379,9 @@ def get_comprehensive_analysis(image_path: str) -> Optional[Dict[str, Any]]:
289
  "scores": {
290
  "hydration": compute_hydration_score(raw_analysis.get("hydration")),
291
  "pigmentation": compute_pigmentation_score(raw_analysis.get("pigmentation")),
292
- "acne": compute_acne_score(raw_analysis.get("acne"))
 
 
293
  },
294
  "age_analysis": raw_analysis.get("age_analysis"),
295
  "metadata": {
@@ -302,7 +394,6 @@ def get_comprehensive_analysis(image_path: str) -> Optional[Dict[str, Any]]:
302
 
303
 
304
  # ==================== LEGACY COMPATIBILITY FUNCTIONS ====================
305
- # These functions maintain backward compatibility with your existing code
306
 
307
  def get_hydration_factors(image_path: str) -> Optional[Dict[str, float]]:
308
  """Legacy function - extracts hydration from complete analysis"""
@@ -322,6 +413,18 @@ def get_acne_factors(image_path: str) -> Optional[Dict[str, float]]:
322
  return result.get("acne") if result else None
323
 
324
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  def get_fitzpatrick_type(image_path: str) -> Optional[int]:
326
  """Legacy function - extracts Fitzpatrick type from complete analysis"""
327
  result = analyze_skin_complete(image_path)
@@ -346,6 +449,111 @@ def get_skin_age(image_path: str) -> Optional[int]:
346
  return None
347
 
348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  # ==================== USAGE STATISTICS ====================
350
 
351
  def get_usage_stats() -> Dict[str, Any]:
@@ -379,10 +587,10 @@ def clear_cache():
379
  # ==================== EXAMPLE USAGE ====================
380
 
381
  if __name__ == "__main__":
382
- # Example 1: Get complete analysis (RECOMMENDED)
383
- print("=" * 60)
384
- print("COMPLETE SKIN ANALYSIS")
385
- print("=" * 60)
386
 
387
  image_path = "path/to/face_image.jpg"
388
 
@@ -391,12 +599,21 @@ if __name__ == "__main__":
391
 
392
  if analysis:
393
  print("\n✓ Analysis successful!")
394
- print(f"\nHydration Score: {analysis['scores']['hydration']}/100")
395
- print(f"Pigmentation Score: {analysis['scores']['pigmentation']}/100")
396
- print(f"Acne Score: {analysis['scores']['acne']}/100")
397
- print(f"\nFitzpatrick Type: {analysis['age_analysis']['fitzpatrick_type']}")
398
- print(f"Eye Age: {analysis['age_analysis']['eye_age']}")
399
- print(f"Skin Age: {analysis['age_analysis']['skin_age']}")
 
 
 
 
 
 
 
 
 
400
 
401
  # Save to file
402
  with open("analysis_result.json", "w") as f:
@@ -405,10 +622,49 @@ if __name__ == "__main__":
405
  else:
406
  print("\n❌ Analysis failed")
407
 
408
- # Example 2: Using legacy functions (still works, uses same cached data)
409
- print("\n" + "=" * 60)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
  print("USING LEGACY FUNCTIONS")
411
- print("=" * 60)
412
 
413
  hydration = get_hydration_factors(image_path)
414
  print(f"\nHydration factors: {hydration}")
@@ -416,9 +672,15 @@ if __name__ == "__main__":
416
  pigmentation = get_pigmentation_factors(image_path)
417
  print(f"Pigmentation factors: {pigmentation}")
418
 
419
- # Example 3: Check usage statistics
420
- print("\n" + "=" * 60)
 
 
 
 
 
 
421
  print("API USAGE STATISTICS")
422
- print("=" * 60)
423
  stats = get_usage_stats()
424
  print(json.dumps(stats, indent=2))
 
89
  max_retries: int = 3
90
  ) -> Optional[Dict[str, Any]]:
91
  """
92
+ Complete skin analysis with ONE API call including pores and wrinkles.
93
 
94
  Args:
95
  image_path: Path to the facial image
 
101
  """
102
 
103
  # Check cache first
104
+ cache_key = f"complete_v2_{get_image_hash(image_path)}"
105
  if use_cache and cache_key in _analysis_cache:
106
  print("✓ Using cached analysis results")
107
  return _analysis_cache[cache_key]
 
115
 
116
  image_part = Part.from_bytes(data=image_bytes, mime_type="image/jpeg")
117
 
118
+ # Combined comprehensive prompt with pores and wrinkles
119
  prompt = """
120
  You are an advanced AI skin analysis system. Analyze the face in this image comprehensively.
121
 
 
147
  "scarring": float (0.0-1.0, acne scarring),
148
  "congestion": float (0.0-1.0, pore congestion)
149
  },
150
+ "pores": {
151
+ "visibility": float (0.0-1.0, how visible/prominent pores are),
152
+ "size": float (0.0-1.0, average pore size - larger is worse),
153
+ "enlarged_pores": float (0.0-1.0, percentage of enlarged pores),
154
+ "clogged_pores": float (0.0-1.0, degree of pore clogging),
155
+ "texture_roughness": float (0.0-1.0, roughness due to pores),
156
+ "t_zone_prominence": float (0.0-1.0, pore visibility in T-zone),
157
+ "cheek_prominence": float (0.0-1.0, pore visibility on cheeks)
158
+ },
159
+ "wrinkles": {
160
+ "forehead_lines": float (0.0-1.0, horizontal forehead wrinkles),
161
+ "frown_lines": float (0.0-1.0, glabellar lines between eyebrows),
162
+ "crows_feet": float (0.0-1.0, eye corner wrinkles),
163
+ "nasolabial_folds": float (0.0-1.0, nose-to-mouth lines),
164
+ "marionette_lines": float (0.0-1.0, mouth-to-chin lines),
165
+ "under_eye_wrinkles": float (0.0-1.0, fine lines under eyes),
166
+ "lip_lines": float (0.0-1.0, perioral wrinkles around mouth),
167
+ "neck_lines": float (0.0-1.0, horizontal neck wrinkles if visible),
168
+ "overall_severity": float (0.0-1.0, overall wrinkle severity),
169
+ "depth": float (0.0-1.0, average depth of wrinkles),
170
+ "dynamic_wrinkles": float (0.0-1.0, expression-related wrinkles),
171
+ "static_wrinkles": float (0.0-1.0, wrinkles at rest)
172
+ },
173
  "age_analysis": {
174
  "fitzpatrick_type": integer (1-6, skin type based on melanin),
175
  "eye_age": integer (estimated age of eye area),
 
177
  }
178
  }
179
 
180
+ DETAILED ANALYSIS GUIDELINES:
181
+
182
+ PORES:
183
+ - Assess pore visibility across different facial zones
184
+ - Consider pore size relative to skin type
185
+ - Note if pores appear stretched, enlarged, or clogged
186
+ - T-zone (forehead, nose, chin) typically has more prominent pores
187
+ - Cheeks may show different pore characteristics
188
+
189
+ WRINKLES:
190
+ - Distinguish between dynamic (expression) and static (at rest) wrinkles
191
+ - Forehead lines: horizontal lines across forehead
192
+ - Frown lines: vertical lines between eyebrows (11 lines)
193
+ - Crow's feet: radiating lines from outer eye corners
194
+ - Nasolabial folds: lines from nose to mouth corners
195
+ - Marionette lines: lines from mouth corners downward
196
+ - Assess depth (superficial vs deep wrinkles)
197
+ - Consider fine lines vs established wrinkles
198
+
199
  CRITICAL RULES:
200
  - Return ONLY raw JSON, no markdown formatting
201
  - No explanations, no text outside JSON
 
204
  - Base analysis ONLY on visible features in the image
205
  - Do NOT guess or infer anything not visible
206
  - Ensure all fields are present in the response
207
+ - If a feature is not visible or applicable, use 0.0
208
  """
209
 
210
  # Make API call
 
277
  def compute_pigmentation_score(pigmentation_factors: Dict[str, float]) -> Optional[float]:
278
  """
279
  Calculate pigmentation score (0-100) from factors
280
+ Higher score = more pigmentation issues
281
  """
282
  if not pigmentation_factors:
283
  return None
 
299
  def compute_acne_score(acne_factors: Dict[str, float]) -> Optional[float]:
300
  """
301
  Calculate acne score (0-100) from factors
302
+ Higher score = more acne issues
303
  """
304
  if not acne_factors:
305
  return None
 
317
  return None
318
 
319
 
320
+ def compute_pores_score(pores_factors: Dict[str, float]) -> Optional[float]:
321
+ """
322
+ Calculate pores score (0-100) from factors
323
+ Higher score = more visible/problematic pores
324
+ """
325
+ if not pores_factors:
326
+ return None
327
+
328
+ try:
329
+ score = (
330
+ pores_factors["visibility"] * 25 +
331
+ pores_factors["size"] * 25 +
332
+ pores_factors["enlarged_pores"] * 20 +
333
+ pores_factors["clogged_pores"] * 15 +
334
+ pores_factors["texture_roughness"] * 15
335
+ )
336
+ return round(score, 1)
337
+ except (KeyError, TypeError):
338
+ return None
339
+
340
+
341
+ def compute_wrinkles_score(wrinkles_factors: Dict[str, float]) -> Optional[float]:
342
+ """
343
+ Calculate wrinkles score (0-100) from factors
344
+ Higher score = more severe wrinkling
345
+ """
346
+ if not wrinkles_factors:
347
+ return None
348
+
349
+ try:
350
+ score = (
351
+ wrinkles_factors["overall_severity"] * 30 +
352
+ wrinkles_factors["depth"] * 20 +
353
+ wrinkles_factors["forehead_lines"] * 10 +
354
+ wrinkles_factors["crows_feet"] * 10 +
355
+ wrinkles_factors["nasolabial_folds"] * 10 +
356
+ wrinkles_factors["frown_lines"] * 8 +
357
+ wrinkles_factors["static_wrinkles"] * 7 +
358
+ wrinkles_factors["under_eye_wrinkles"] * 5
359
+ )
360
+ return round(score, 1)
361
+ except (KeyError, TypeError):
362
+ return None
363
+
364
+
365
  def get_comprehensive_analysis(image_path: str) -> Optional[Dict[str, Any]]:
366
  """
367
+ Get complete skin analysis with computed scores including pores and wrinkles.
368
 
369
  Returns a dictionary with all raw factors plus computed scores.
370
  """
 
379
  "scores": {
380
  "hydration": compute_hydration_score(raw_analysis.get("hydration")),
381
  "pigmentation": compute_pigmentation_score(raw_analysis.get("pigmentation")),
382
+ "acne": compute_acne_score(raw_analysis.get("acne")),
383
+ "pores": compute_pores_score(raw_analysis.get("pores")),
384
+ "wrinkles": compute_wrinkles_score(raw_analysis.get("wrinkles"))
385
  },
386
  "age_analysis": raw_analysis.get("age_analysis"),
387
  "metadata": {
 
394
 
395
 
396
  # ==================== LEGACY COMPATIBILITY FUNCTIONS ====================
 
397
 
398
  def get_hydration_factors(image_path: str) -> Optional[Dict[str, float]]:
399
  """Legacy function - extracts hydration from complete analysis"""
 
413
  return result.get("acne") if result else None
414
 
415
 
416
+ def get_pores_factors(image_path: str) -> Optional[Dict[str, float]]:
417
+ """Get pores analysis from complete analysis"""
418
+ result = analyze_skin_complete(image_path)
419
+ return result.get("pores") if result else None
420
+
421
+
422
+ def get_wrinkles_factors(image_path: str) -> Optional[Dict[str, float]]:
423
+ """Get wrinkles analysis from complete analysis"""
424
+ result = analyze_skin_complete(image_path)
425
+ return result.get("wrinkles") if result else None
426
+
427
+
428
  def get_fitzpatrick_type(image_path: str) -> Optional[int]:
429
  """Legacy function - extracts Fitzpatrick type from complete analysis"""
430
  result = analyze_skin_complete(image_path)
 
449
  return None
450
 
451
 
452
+ # ==================== DETAILED ANALYSIS FUNCTIONS ====================
453
+
454
+ def get_wrinkle_breakdown(image_path: str) -> Optional[Dict[str, Any]]:
455
+ """
456
+ Get detailed breakdown of wrinkles by type and severity
457
+ """
458
+ wrinkles = get_wrinkles_factors(image_path)
459
+ if not wrinkles:
460
+ return None
461
+
462
+ # Categorize wrinkles by location
463
+ upper_face = {
464
+ "forehead_lines": wrinkles.get("forehead_lines", 0),
465
+ "frown_lines": wrinkles.get("frown_lines", 0),
466
+ "average": (wrinkles.get("forehead_lines", 0) + wrinkles.get("frown_lines", 0)) / 2
467
+ }
468
+
469
+ eye_area = {
470
+ "crows_feet": wrinkles.get("crows_feet", 0),
471
+ "under_eye_wrinkles": wrinkles.get("under_eye_wrinkles", 0),
472
+ "average": (wrinkles.get("crows_feet", 0) + wrinkles.get("under_eye_wrinkles", 0)) / 2
473
+ }
474
+
475
+ lower_face = {
476
+ "nasolabial_folds": wrinkles.get("nasolabial_folds", 0),
477
+ "marionette_lines": wrinkles.get("marionette_lines", 0),
478
+ "lip_lines": wrinkles.get("lip_lines", 0),
479
+ "average": (wrinkles.get("nasolabial_folds", 0) +
480
+ wrinkles.get("marionette_lines", 0) +
481
+ wrinkles.get("lip_lines", 0)) / 3
482
+ }
483
+
484
+ # Categorize by type
485
+ wrinkle_types = {
486
+ "dynamic": wrinkles.get("dynamic_wrinkles", 0),
487
+ "static": wrinkles.get("static_wrinkles", 0),
488
+ "predominant_type": "static" if wrinkles.get("static_wrinkles", 0) > wrinkles.get("dynamic_wrinkles", 0) else "dynamic"
489
+ }
490
+
491
+ return {
492
+ "by_location": {
493
+ "upper_face": upper_face,
494
+ "eye_area": eye_area,
495
+ "lower_face": lower_face
496
+ },
497
+ "by_type": wrinkle_types,
498
+ "severity": {
499
+ "depth": wrinkles.get("depth", 0),
500
+ "overall": wrinkles.get("overall_severity", 0)
501
+ },
502
+ "most_affected_area": max(
503
+ [("upper_face", upper_face["average"]),
504
+ ("eye_area", eye_area["average"]),
505
+ ("lower_face", lower_face["average"])],
506
+ key=lambda x: x[1]
507
+ )[0]
508
+ }
509
+
510
+
511
+ def get_pores_breakdown(image_path: str) -> Optional[Dict[str, Any]]:
512
+ """
513
+ Get detailed breakdown of pores by zone and characteristics
514
+ """
515
+ pores = get_pores_factors(image_path)
516
+ if not pores:
517
+ return None
518
+
519
+ # Analyze by facial zone
520
+ zones = {
521
+ "t_zone": {
522
+ "prominence": pores.get("t_zone_prominence", 0),
523
+ "severity": "severe" if pores.get("t_zone_prominence", 0) > 0.7
524
+ else "moderate" if pores.get("t_zone_prominence", 0) > 0.4
525
+ else "mild"
526
+ },
527
+ "cheeks": {
528
+ "prominence": pores.get("cheek_prominence", 0),
529
+ "severity": "severe" if pores.get("cheek_prominence", 0) > 0.7
530
+ else "moderate" if pores.get("cheek_prominence", 0) > 0.4
531
+ else "mild"
532
+ }
533
+ }
534
+
535
+ # Overall pore characteristics
536
+ characteristics = {
537
+ "size": pores.get("size", 0),
538
+ "visibility": pores.get("visibility", 0),
539
+ "enlarged_percentage": pores.get("enlarged_pores", 0) * 100,
540
+ "clogging_level": pores.get("clogged_pores", 0)
541
+ }
542
+
543
+ # Recommendations based on severity
544
+ severity_score = (pores.get("visibility", 0) + pores.get("size", 0)) / 2
545
+
546
+ return {
547
+ "by_zone": zones,
548
+ "characteristics": characteristics,
549
+ "texture_impact": pores.get("texture_roughness", 0),
550
+ "overall_severity": "severe" if severity_score > 0.7
551
+ else "moderate" if severity_score > 0.4
552
+ else "mild",
553
+ "most_affected_zone": "t_zone" if zones["t_zone"]["prominence"] > zones["cheeks"]["prominence"] else "cheeks"
554
+ }
555
+
556
+
557
  # ==================== USAGE STATISTICS ====================
558
 
559
  def get_usage_stats() -> Dict[str, Any]:
 
587
  # ==================== EXAMPLE USAGE ====================
588
 
589
  if __name__ == "__main__":
590
+ # Example 1: Get complete analysis with pores and wrinkles
591
+ print("=" * 70)
592
+ print("COMPLETE SKIN ANALYSIS (INCLUDING PORES & WRINKLES)")
593
+ print("=" * 70)
594
 
595
  image_path = "path/to/face_image.jpg"
596
 
 
599
 
600
  if analysis:
601
  print("\n✓ Analysis successful!")
602
+ print(f"\n{'='*70}")
603
+ print("SCORES (0-100, higher = more concern)")
604
+ print(f"{'='*70}")
605
+ print(f"Hydration Score: {analysis['scores']['hydration']:.1f}/100")
606
+ print(f"Pigmentation Score: {analysis['scores']['pigmentation']:.1f}/100")
607
+ print(f"Acne Score: {analysis['scores']['acne']:.1f}/100")
608
+ print(f"Pores Score: {analysis['scores']['pores']:.1f}/100")
609
+ print(f"Wrinkles Score: {analysis['scores']['wrinkles']:.1f}/100")
610
+
611
+ print(f"\n{'='*70}")
612
+ print("AGE ANALYSIS")
613
+ print(f"{'='*70}")
614
+ print(f"Fitzpatrick Type: {analysis['age_analysis']['fitzpatrick_type']}")
615
+ print(f"Eye Age: {analysis['age_analysis']['eye_age']} years")
616
+ print(f"Skin Age: {analysis['age_analysis']['skin_age']} years")
617
 
618
  # Save to file
619
  with open("analysis_result.json", "w") as f:
 
622
  else:
623
  print("\n❌ Analysis failed")
624
 
625
+ # Example 2: Get detailed pores breakdown
626
+ print("\n" + "=" * 70)
627
+ print("DETAILED PORES ANALYSIS")
628
+ print("=" * 70)
629
+
630
+ pores_detail = get_pores_breakdown(image_path)
631
+ if pores_detail:
632
+ print(f"\nMost Affected Zone: {pores_detail['most_affected_zone'].upper()}")
633
+ print(f"Overall Severity: {pores_detail['overall_severity'].upper()}")
634
+ print(f"\nT-Zone Prominence: {pores_detail['by_zone']['t_zone']['prominence']:.2f}")
635
+ print(f"Cheek Prominence: {pores_detail['by_zone']['cheeks']['prominence']:.2f}")
636
+ print(f"Enlarged Pores: {pores_detail['characteristics']['enlarged_percentage']:.1f}%")
637
+ print(f"Pore Size: {pores_detail['characteristics']['size']:.2f}")
638
+ print(f"Visibility: {pores_detail['characteristics']['visibility']:.2f}")
639
+
640
+ # Example 3: Get detailed wrinkles breakdown
641
+ print("\n" + "=" * 70)
642
+ print("DETAILED WRINKLES ANALYSIS")
643
+ print("=" * 70)
644
+
645
+ wrinkles_detail = get_wrinkle_breakdown(image_path)
646
+ if wrinkles_detail:
647
+ print(f"\nMost Affected Area: {wrinkles_detail['most_affected_area'].upper()}")
648
+ print(f"Predominant Type: {wrinkles_detail['by_type']['predominant_type'].upper()}")
649
+ print(f"Overall Severity: {wrinkles_detail['severity']['overall']:.2f}")
650
+ print(f"Average Depth: {wrinkles_detail['severity']['depth']:.2f}")
651
+
652
+ print(f"\nUpper Face (avg): {wrinkles_detail['by_location']['upper_face']['average']:.2f}")
653
+ print(f" - Forehead: {wrinkles_detail['by_location']['upper_face']['forehead_lines']:.2f}")
654
+ print(f" - Frown Lines: {wrinkles_detail['by_location']['upper_face']['frown_lines']:.2f}")
655
+
656
+ print(f"\nEye Area (avg): {wrinkles_detail['by_location']['eye_area']['average']:.2f}")
657
+ print(f" - Crow's Feet: {wrinkles_detail['by_location']['eye_area']['crows_feet']:.2f}")
658
+ print(f" - Under Eye: {wrinkles_detail['by_location']['eye_area']['under_eye_wrinkles']:.2f}")
659
+
660
+ print(f"\nLower Face (avg): {wrinkles_detail['by_location']['lower_face']['average']:.2f}")
661
+ print(f" - Nasolabial: {wrinkles_detail['by_location']['lower_face']['nasolabial_folds']:.2f}")
662
+ print(f" - Marionette: {wrinkles_detail['by_location']['lower_face']['marionette_lines']:.2f}")
663
+
664
+ # Example 4: Using legacy functions (still works, uses same cached data)
665
+ print("\n" + "=" * 70)
666
  print("USING LEGACY FUNCTIONS")
667
+ print("=" * 70)
668
 
669
  hydration = get_hydration_factors(image_path)
670
  print(f"\nHydration factors: {hydration}")
 
672
  pigmentation = get_pigmentation_factors(image_path)
673
  print(f"Pigmentation factors: {pigmentation}")
674
 
675
+ pores = get_pores_factors(image_path)
676
+ print(f"Pores factors: {pores}")
677
+
678
+ wrinkles = get_wrinkles_factors(image_path)
679
+ print(f"Wrinkles factors: {wrinkles}")
680
+
681
+ # Example 5: Check usage statistics
682
+ print("\n" + "=" * 70)
683
  print("API USAGE STATISTICS")
684
+ print("=" * 70)
685
  stats = get_usage_stats()
686
  print(json.dumps(stats, indent=2))