chawin.chen commited on
Commit
bf03b10
·
1 Parent(s): 7a6cb13
Files changed (2) hide show
  1. config.py +2 -0
  2. face_analyzer.py +66 -30
config.py CHANGED
@@ -298,6 +298,8 @@ IMG_QUALITY = float(os.environ.get("IMG_QUALITY", 0.5))
298
  FACE_CONFIDENCE = float(os.environ.get("FACE_CONFIDENCE", 0.7))
299
  AGE_CONFIDENCE = float(os.environ.get("AGE_CONFIDENCE", 0.99))
300
  GENDER_CONFIDENCE = float(os.environ.get("GENDER_CONFIDENCE", 1.1))
 
 
301
  UPSCALE_SIZE = int(os.environ.get("UPSCALE_SIZE", 2))
302
  SAVE_QUALITY = int(os.environ.get("SAVE_QUALITY", 85))
303
  REALESRGAN_MODEL = os.environ.get("REALESRGAN_MODEL", "realesr-general-x4v3")
 
298
  FACE_CONFIDENCE = float(os.environ.get("FACE_CONFIDENCE", 0.7))
299
  AGE_CONFIDENCE = float(os.environ.get("AGE_CONFIDENCE", 0.99))
300
  GENDER_CONFIDENCE = float(os.environ.get("GENDER_CONFIDENCE", 1.1))
301
+ # 是否启用 DeepFace 的情绪识别(默认开启;关闭可减少推理耗时)
302
+ DEEPFACE_EMOTION_ENABLED = os.environ.get("DEEPFACE_EMOTION_ENABLED", "true").lower() in ("1", "true", "on")
303
  UPSCALE_SIZE = int(os.environ.get("UPSCALE_SIZE", 2))
304
  SAVE_QUALITY = int(os.environ.get("SAVE_QUALITY", 85))
305
  REALESRGAN_MODEL = os.environ.get("REALESRGAN_MODEL", "realesr-general-x4v3")
face_analyzer.py CHANGED
@@ -1,7 +1,7 @@
1
  import os
2
  import random
3
  import time
4
- from typing import List, Dict, Any
5
 
6
  import cv2
7
  import numpy as np
@@ -485,7 +485,7 @@ class EnhancedFaceAnalyzer:
485
  raise e
486
 
487
  def _predict_age_emotion_with_deepface(
488
- self, face_image: np.ndarray
489
  ) -> Dict[str, Any]:
490
  """使用DeepFace预测年龄、情绪(并返回可用的性别信息用于回退)"""
491
  if not DEEPFACE_AVAILABLE:
@@ -496,10 +496,11 @@ class EnhancedFaceAnalyzer:
496
  raise ValueError("无效的人脸图像")
497
 
498
  try:
 
499
  # DeepFace分析 - 禁用进度条和详细输出
500
  result = DeepFace.analyze(
501
  img_path=face_image,
502
- actions=["age", "emotion", "gender"],
503
  enforce_detection=False,
504
  detector_backend="skip",
505
  silent=True # 禁用进度条输出
@@ -511,8 +512,12 @@ class EnhancedFaceAnalyzer:
511
 
512
  # 提取信息
513
  age = result.get("age", 25)
514
- emotion = result.get("dominant_emotion", "neutral")
515
- emotion_scores = result.get("emotion", {})
 
 
 
 
516
  # 性别信息(用于在HowCuteAmI置信度低时回退)
517
  deep_gender = result.get("dominant_gender", "Woman")
518
  deep_gender_conf = result.get("gender", {}).get(deep_gender, 50.0) / 100.0
@@ -576,6 +581,10 @@ class EnhancedFaceAnalyzer:
576
  # 使用HowCuteAmI预测颜值和性别
577
  beauty_gender_result = self._predict_beauty_gender_with_howcuteami(face)
578
 
 
 
 
 
579
  # 首先获取HowCuteAmI的年龄/性别预测置信度
580
  howcuteami_age_confidence = beauty_gender_result.get("age_confidence", 0)
581
  gender_confidence = beauty_gender_result.get("gender_confidence", 0)
@@ -586,9 +595,9 @@ class EnhancedFaceAnalyzer:
586
  # 如果HowCuteAmI的年龄置信度低于阈值,则使用DeepFace的年龄
587
  agec = config.AGE_CONFIDENCE
588
  if howcuteami_age_confidence < agec:
589
- # 使用DeepFace获取年龄/情绪(以及可选的性别回退信息)
590
  age_emotion_result = self._predict_age_emotion_with_deepface(
591
- face_image
592
  )
593
  deep_age = age_emotion_result["age"]
594
  logger.info(
@@ -602,8 +611,8 @@ class EnhancedFaceAnalyzer:
602
  "beauty_raw_score": beauty_gender_result["beauty_raw_score"],
603
  "age": deep_age,
604
  "age_confidence": age_emotion_result["age_confidence"],
605
- "emotion": age_emotion_result["emotion"],
606
- "emotion_analysis": age_emotion_result["emotion_analysis"],
607
  "model_used": "hybrid_deepface_age",
608
  "age_model_used": "DeepFace",
609
  "gender_model_used": "HowCuteAmI",
@@ -613,6 +622,16 @@ class EnhancedFaceAnalyzer:
613
  logger.info(
614
  f"HowCuteAmI age confidence ({howcuteami_age_confidence}) is high enough, value={age}; using HowCuteAmI for age prediction"
615
  )
 
 
 
 
 
 
 
 
 
 
616
  # 合并结果,保留HowCuteAmI的年龄预测
617
  result = {
618
  "gender": beauty_gender_result["gender"], # 先用HowCuteAmI,后面可能回退
@@ -621,8 +640,8 @@ class EnhancedFaceAnalyzer:
621
  "beauty_raw_score": beauty_gender_result["beauty_raw_score"],
622
  "age": beauty_gender_result["age"],
623
  "age_confidence": beauty_gender_result["age_confidence"],
624
- "emotion": None,
625
- "emotion_analysis": None,
626
  "model_used": "hybrid",
627
  "age_model_used": "HowCuteAmI",
628
  "gender_model_used": "HowCuteAmI",
@@ -632,8 +651,8 @@ class EnhancedFaceAnalyzer:
632
  try:
633
  how_gender = beauty_gender_result.get("gender")
634
  how_conf = float(beauty_gender_result.get("gender_confidence", 0) or 0)
635
- deep_gender = age_emotion_result.get("gender")
636
- deep_conf = float(age_emotion_result.get("gender_confidence", 0) or 0)
637
 
638
  final_gender = result.get("gender")
639
  final_conf = float(result.get("gender_confidence", 0) or 0)
@@ -714,10 +733,12 @@ class EnhancedFaceAnalyzer:
714
  raise ValueError("无效的人脸图像")
715
 
716
  try:
 
 
717
  # DeepFace分析 - 禁用进度条和详细输出
718
  result = DeepFace.analyze(
719
  img_path=face_image,
720
- actions=["age", "gender", "emotion"],
721
  enforce_detection=False,
722
  detector_backend="skip",
723
  silent=True # 禁用进度条输出
@@ -740,8 +761,12 @@ class EnhancedFaceAnalyzer:
740
  gender = "Male"
741
 
742
  # DeepFace没有内置颜值评分,这里使用简单的启发式方法
743
- emotion = result.get("dominant_emotion", "neutral")
744
- emotion_scores = result.get("emotion", {})
 
 
 
 
745
 
746
  # 基于情绪和年龄的简单颜值估算
747
  happiness_score = emotion_scores.get("happy", 0) / 100
@@ -835,10 +860,10 @@ class EnhancedFaceAnalyzer:
835
  )
836
  elif model_type == ModelType.HOWCUTEAMI:
837
  prediction_result = self._predict_with_howcuteami(face_resized)
838
- # 非混合模式也进行性别合并:引入DeepFace性别
839
  try:
840
  age_emotion_result = self._predict_age_emotion_with_deepface(
841
- face_for_deepface
842
  )
843
  how_gender = prediction_result.get("gender")
844
  how_conf = float(prediction_result.get("gender_confidence", 0) or 0)
@@ -848,13 +873,17 @@ class EnhancedFaceAnalyzer:
848
  final_conf = float(prediction_result.get("gender_confidence", 0) or 0)
849
  if (str(how_gender) == "Female") or (str(deep_gender) == "Female"):
850
  final_gender = "Female"
851
- final_conf = max(how_conf if how_gender == "Female" else 0,
852
- deep_conf if deep_gender == "Female" else 0)
 
 
853
  prediction_result["gender_model_used"] = "Combined(H+DF)"
854
  elif (str(how_gender) == "Male") and (str(deep_gender) == "Male"):
855
  final_gender = "Male"
856
- final_conf = max(how_conf if how_gender == "Male" else 0,
857
- deep_conf if deep_gender == "Male" else 0)
 
 
858
  prediction_result["gender_model_used"] = "Combined(H+DF)"
859
  prediction_result["gender"] = final_gender
860
  prediction_result["gender_confidence"] = round(float(final_conf), 4)
@@ -875,13 +904,17 @@ class EnhancedFaceAnalyzer:
875
  final_conf = float(prediction_result.get("gender_confidence", 0) or 0)
876
  if (str(how_gender) == "Female") or (str(deep_gender) == "Female"):
877
  final_gender = "Female"
878
- final_conf = max(how_conf if how_gender == "Female" else 0,
879
- deep_conf if deep_gender == "Female" else 0)
 
 
880
  prediction_result["gender_model_used"] = "Combined(H+DF)"
881
  elif (str(how_gender) == "Male") and (str(deep_gender) == "Male"):
882
  final_gender = "Male"
883
- final_conf = max(how_conf if how_gender == "Male" else 0,
884
- deep_conf if deep_gender == "Male" else 0)
 
 
885
  prediction_result["gender_model_used"] = "Combined(H+DF)"
886
  prediction_result["gender"] = final_gender
887
  prediction_result["gender_confidence"] = round(float(final_conf), 4)
@@ -892,7 +925,9 @@ class EnhancedFaceAnalyzer:
892
  prediction_result = self._predict_with_hybrid_model(
893
  face_resized, face_for_deepface
894
  )
895
- logger.warning(f"Model {model_type.value} is not available, using hybrid mode")
 
 
896
 
897
  except Exception as e:
898
  logger.error(f"Prediction failed, using default values: {e}")
@@ -1016,8 +1051,8 @@ class EnhancedFaceAnalyzer:
1016
  "age_model_used": prediction_result.get("age_model_used", prediction_result.get("model_used", model_type.value)),
1017
  "beauty_score": prediction_result.get("beauty_score", 0),
1018
  "beauty_raw_score": prediction_result.get("beauty_raw_score", 0),
1019
- "emotion": prediction_result.get("emotion", "neutral"),
1020
- "emotion_analysis": prediction_result.get("emotion_analysis", {}),
1021
  # "facial_features": facial_features, # 五官分析
1022
  "bounding_box": {
1023
  "x1": int(face_box[0]),
@@ -1054,12 +1089,13 @@ class EnhancedFaceAnalyzer:
1054
  if DEEPFACE_AVAILABLE:
1055
  try:
1056
  import tempfile
 
1057
  with tempfile.NamedTemporaryFile(suffix='.webp', delete=False) as tmp_file:
1058
  cv2.imwrite(tmp_file.name, test_image, [cv2.IMWRITE_WEBP_QUALITY, 95])
1059
  # 预热DeepFace - 使用最小的actions集合
1060
  DeepFace.analyze(
1061
  img_path=tmp_file.name,
1062
- actions=["age", "emotion", "gender"],
1063
  detector_backend="yolov8",
1064
  enforce_detection=False,
1065
  silent=True
 
1
  import os
2
  import random
3
  import time
4
+ from typing import List, Dict, Any, Optional
5
 
6
  import cv2
7
  import numpy as np
 
485
  raise e
486
 
487
  def _predict_age_emotion_with_deepface(
488
+ self, face_image: np.ndarray, include_emotion: bool = True
489
  ) -> Dict[str, Any]:
490
  """使用DeepFace预测年龄、情绪(并返回可用的性别信息用于回退)"""
491
  if not DEEPFACE_AVAILABLE:
 
496
  raise ValueError("无效的人脸图像")
497
 
498
  try:
499
+ actions = ["age", "gender", "emotion"] if include_emotion else ["age", "gender"]
500
  # DeepFace分析 - 禁用进度条和详细输出
501
  result = DeepFace.analyze(
502
  img_path=face_image,
503
+ actions=actions,
504
  enforce_detection=False,
505
  detector_backend="skip",
506
  silent=True # 禁用进度条输出
 
512
 
513
  # 提取信息
514
  age = result.get("age", 25)
515
+ if include_emotion:
516
+ emotion = result.get("dominant_emotion", "neutral")
517
+ emotion_scores = result.get("emotion", {}) or {}
518
+ else:
519
+ emotion = "neutral"
520
+ emotion_scores = {"neutral": 100.0}
521
  # 性别信息(用于在HowCuteAmI置信度低时回退)
522
  deep_gender = result.get("dominant_gender", "Woman")
523
  deep_gender_conf = result.get("gender", {}).get(deep_gender, 50.0) / 100.0
 
581
  # 使用HowCuteAmI预测颜值和性别
582
  beauty_gender_result = self._predict_beauty_gender_with_howcuteami(face)
583
 
584
+ # Hybrid 模式下可配置是否启用 DeepFace 情绪识别(默认启用)。
585
+ deepface_emotion_enabled = bool(getattr(config, "DEEPFACE_EMOTION_ENABLED", True))
586
+ age_emotion_result: Optional[Dict[str, Any]] = None
587
+
588
  # 首先获取HowCuteAmI的年龄/性别预测置信度
589
  howcuteami_age_confidence = beauty_gender_result.get("age_confidence", 0)
590
  gender_confidence = beauty_gender_result.get("gender_confidence", 0)
 
595
  # 如果HowCuteAmI的年龄置信度低于阈值,则使用DeepFace的年龄
596
  agec = config.AGE_CONFIDENCE
597
  if howcuteami_age_confidence < agec:
598
+ # 需要 DeepFace 年龄时,仍调用 DeepFace(但可按开关选择是否同时跑 emotion)
599
  age_emotion_result = self._predict_age_emotion_with_deepface(
600
+ face_image, include_emotion=deepface_emotion_enabled
601
  )
602
  deep_age = age_emotion_result["age"]
603
  logger.info(
 
611
  "beauty_raw_score": beauty_gender_result["beauty_raw_score"],
612
  "age": deep_age,
613
  "age_confidence": age_emotion_result["age_confidence"],
614
+ "emotion": age_emotion_result.get("emotion") or "neutral",
615
+ "emotion_analysis": age_emotion_result.get("emotion_analysis") or {"neutral": 100.0},
616
  "model_used": "hybrid_deepface_age",
617
  "age_model_used": "DeepFace",
618
  "gender_model_used": "HowCuteAmI",
 
622
  logger.info(
623
  f"HowCuteAmI age confidence ({howcuteami_age_confidence}) is high enough, value={age}; using HowCuteAmI for age prediction"
624
  )
625
+ # 情绪识别完全可选:关闭时直接返回默认值,避免多一次 DeepFace 推理。
626
+ if deepface_emotion_enabled:
627
+ age_emotion_result = self._predict_age_emotion_with_deepface(
628
+ face_image, include_emotion=True
629
+ )
630
+ emotion = age_emotion_result.get("emotion") or "neutral"
631
+ emotion_analysis = age_emotion_result.get("emotion_analysis") or {"neutral": 100.0}
632
+ else:
633
+ emotion = "neutral"
634
+ emotion_analysis = {"neutral": 100.0}
635
  # 合并结果,保留HowCuteAmI的年龄预测
636
  result = {
637
  "gender": beauty_gender_result["gender"], # 先用HowCuteAmI,后面可能回退
 
640
  "beauty_raw_score": beauty_gender_result["beauty_raw_score"],
641
  "age": beauty_gender_result["age"],
642
  "age_confidence": beauty_gender_result["age_confidence"],
643
+ "emotion": emotion,
644
+ "emotion_analysis": emotion_analysis,
645
  "model_used": "hybrid",
646
  "age_model_used": "HowCuteAmI",
647
  "gender_model_used": "HowCuteAmI",
 
651
  try:
652
  how_gender = beauty_gender_result.get("gender")
653
  how_conf = float(beauty_gender_result.get("gender_confidence", 0) or 0)
654
+ deep_gender = age_emotion_result.get("gender") if age_emotion_result else None
655
+ deep_conf = float(age_emotion_result.get("gender_confidence", 0) or 0) if age_emotion_result else 0.0
656
 
657
  final_gender = result.get("gender")
658
  final_conf = float(result.get("gender_confidence", 0) or 0)
 
733
  raise ValueError("无效的人脸图像")
734
 
735
  try:
736
+ deepface_emotion_enabled = bool(getattr(config, "DEEPFACE_EMOTION_ENABLED", True))
737
+ actions = ["age", "gender", "emotion"] if deepface_emotion_enabled else ["age", "gender"]
738
  # DeepFace分析 - 禁用进度条和详细输出
739
  result = DeepFace.analyze(
740
  img_path=face_image,
741
+ actions=actions,
742
  enforce_detection=False,
743
  detector_backend="skip",
744
  silent=True # 禁用进度条输出
 
761
  gender = "Male"
762
 
763
  # DeepFace没有内置颜值评分,这里使用简单的启发式方法
764
+ if deepface_emotion_enabled:
765
+ emotion = result.get("dominant_emotion", "neutral")
766
+ emotion_scores = result.get("emotion", {}) or {}
767
+ else:
768
+ emotion = "neutral"
769
+ emotion_scores = {"neutral": 100.0}
770
 
771
  # 基于情绪和年龄的简单颜值估算
772
  happiness_score = emotion_scores.get("happy", 0) / 100
 
860
  )
861
  elif model_type == ModelType.HOWCUTEAMI:
862
  prediction_result = self._predict_with_howcuteami(face_resized)
863
+ # 非混合模式也进行性别合并:引入DeepFace性别(不需要 emotion,减少耗时)
864
  try:
865
  age_emotion_result = self._predict_age_emotion_with_deepface(
866
+ face_for_deepface, include_emotion=False
867
  )
868
  how_gender = prediction_result.get("gender")
869
  how_conf = float(prediction_result.get("gender_confidence", 0) or 0)
 
873
  final_conf = float(prediction_result.get("gender_confidence", 0) or 0)
874
  if (str(how_gender) == "Female") or (str(deep_gender) == "Female"):
875
  final_gender = "Female"
876
+ final_conf = max(
877
+ how_conf if how_gender == "Female" else 0,
878
+ deep_conf if deep_gender == "Female" else 0,
879
+ )
880
  prediction_result["gender_model_used"] = "Combined(H+DF)"
881
  elif (str(how_gender) == "Male") and (str(deep_gender) == "Male"):
882
  final_gender = "Male"
883
+ final_conf = max(
884
+ how_conf if how_gender == "Male" else 0,
885
+ deep_conf if deep_gender == "Male" else 0,
886
+ )
887
  prediction_result["gender_model_used"] = "Combined(H+DF)"
888
  prediction_result["gender"] = final_gender
889
  prediction_result["gender_confidence"] = round(float(final_conf), 4)
 
904
  final_conf = float(prediction_result.get("gender_confidence", 0) or 0)
905
  if (str(how_gender) == "Female") or (str(deep_gender) == "Female"):
906
  final_gender = "Female"
907
+ final_conf = max(
908
+ how_conf if how_gender == "Female" else 0,
909
+ deep_conf if deep_gender == "Female" else 0,
910
+ )
911
  prediction_result["gender_model_used"] = "Combined(H+DF)"
912
  elif (str(how_gender) == "Male") and (str(deep_gender) == "Male"):
913
  final_gender = "Male"
914
+ final_conf = max(
915
+ how_conf if how_gender == "Male" else 0,
916
+ deep_conf if deep_gender == "Male" else 0,
917
+ )
918
  prediction_result["gender_model_used"] = "Combined(H+DF)"
919
  prediction_result["gender"] = final_gender
920
  prediction_result["gender_confidence"] = round(float(final_conf), 4)
 
925
  prediction_result = self._predict_with_hybrid_model(
926
  face_resized, face_for_deepface
927
  )
928
+ logger.warning(
929
+ f"Model {model_type.value} is not available, using hybrid mode"
930
+ )
931
 
932
  except Exception as e:
933
  logger.error(f"Prediction failed, using default values: {e}")
 
1051
  "age_model_used": prediction_result.get("age_model_used", prediction_result.get("model_used", model_type.value)),
1052
  "beauty_score": prediction_result.get("beauty_score", 0),
1053
  "beauty_raw_score": prediction_result.get("beauty_raw_score", 0),
1054
+ "emotion": prediction_result.get("emotion") or "neutral",
1055
+ "emotion_analysis": prediction_result.get("emotion_analysis") or {"neutral": 100.0},
1056
  # "facial_features": facial_features, # 五官分析
1057
  "bounding_box": {
1058
  "x1": int(face_box[0]),
 
1089
  if DEEPFACE_AVAILABLE:
1090
  try:
1091
  import tempfile
1092
+ deepface_emotion_enabled = bool(getattr(config, "DEEPFACE_EMOTION_ENABLED", True))
1093
  with tempfile.NamedTemporaryFile(suffix='.webp', delete=False) as tmp_file:
1094
  cv2.imwrite(tmp_file.name, test_image, [cv2.IMWRITE_WEBP_QUALITY, 95])
1095
  # 预热DeepFace - 使用最小的actions集合
1096
  DeepFace.analyze(
1097
  img_path=tmp_file.name,
1098
+ actions=["age", "gender", "emotion"] if deepface_emotion_enabled else ["age", "gender"],
1099
  detector_backend="yolov8",
1100
  enforce_detection=False,
1101
  silent=True