chenchaoyun
commited on
Commit
·
d11ff01
1
Parent(s):
4e14343
fix
Browse files- api_routes.py +1 -1
- app.py +1 -1
- cleanup_scheduler.py +2 -2
- face_analyzer.py +3 -3
- facial_analyzer.py +10 -10
- utils.py +2 -2
- vector_store.py +1 -1
- wx_access_token.py +1 -1
api_routes.py
CHANGED
|
@@ -147,7 +147,7 @@ if DEEPFACE_AVAILABLE:
|
|
| 147 |
outputs = self.model(img, training=False)
|
| 148 |
embeddings = _safe_tensor_to_numpy(outputs)
|
| 149 |
except Exception as call_exc:
|
| 150 |
-
logger.
|
| 151 |
|
| 152 |
if embeddings is None:
|
| 153 |
# Keras 3 调用 self.model(...) 可能返回SymbolicTensor,退回 predict
|
|
|
|
| 147 |
outputs = self.model(img, training=False)
|
| 148 |
embeddings = _safe_tensor_to_numpy(outputs)
|
| 149 |
except Exception as call_exc:
|
| 150 |
+
logger.info(f"DeepFace forward fallback self.model 调用失败,改用 predict: {call_exc}")
|
| 151 |
|
| 152 |
if embeddings is None:
|
| 153 |
# Keras 3 调用 self.model(...) 可能返回SymbolicTensor,退回 predict
|
app.py
CHANGED
|
@@ -73,7 +73,7 @@ async def lifespan(app: FastAPI):
|
|
| 73 |
logger.info(f"DeepFace available: {DEEPFACE_AVAILABLE}")
|
| 74 |
logger.info(f"YOLO available: {YOLO_AVAILABLE}")
|
| 75 |
logger.info(f"MediaPipe available: {DLIB_AVAILABLE}")
|
| 76 |
-
logger.
|
| 77 |
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 78 |
|
| 79 |
# 初始化数据库连接池
|
|
|
|
| 73 |
logger.info(f"DeepFace available: {DEEPFACE_AVAILABLE}")
|
| 74 |
logger.info(f"YOLO available: {YOLO_AVAILABLE}")
|
| 75 |
logger.info(f"MediaPipe available: {DLIB_AVAILABLE}")
|
| 76 |
+
logger.info(f"Archive directory: {IMAGES_DIR}")
|
| 77 |
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 78 |
|
| 79 |
# 初始化数据库连接池
|
cleanup_scheduler.py
CHANGED
|
@@ -74,14 +74,14 @@ class ImageCleanupScheduler:
|
|
| 74 |
deleted_files.append(os.path.basename(file_path))
|
| 75 |
total_size_deleted += file_size
|
| 76 |
|
| 77 |
-
logger.
|
| 78 |
|
| 79 |
except (OSError, IOError) as e:
|
| 80 |
logger.error(f"Failed to delete file {os.path.basename(file_path)}: {e}")
|
| 81 |
continue
|
| 82 |
|
| 83 |
logger.info(f"Cleanup completed! Deleted {len(deleted_files)} files, ")
|
| 84 |
-
logger.
|
| 85 |
else:
|
| 86 |
logger.info("Cleanup completed! No expired files found to clean")
|
| 87 |
|
|
|
|
| 74 |
deleted_files.append(os.path.basename(file_path))
|
| 75 |
total_size_deleted += file_size
|
| 76 |
|
| 77 |
+
logger.info(f"Deleting expired file: {os.path.basename(file_path)} ")
|
| 78 |
|
| 79 |
except (OSError, IOError) as e:
|
| 80 |
logger.error(f"Failed to delete file {os.path.basename(file_path)}: {e}")
|
| 81 |
continue
|
| 82 |
|
| 83 |
logger.info(f"Cleanup completed! Deleted {len(deleted_files)} files, ")
|
| 84 |
+
logger.info(f"Deleted file list: {', '.join(deleted_files[:10])}")
|
| 85 |
else:
|
| 86 |
logger.info("Cleanup completed! No expired files found to clean")
|
| 87 |
|
face_analyzer.py
CHANGED
|
@@ -194,7 +194,7 @@ class EnhancedFaceAnalyzer:
|
|
| 194 |
# 获取边界框坐标 (xyxy格式)
|
| 195 |
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy().astype(int)
|
| 196 |
confidence = float(box.conf[0])
|
| 197 |
-
logger.
|
| 198 |
f"detect class_id={class_id}, confidence={confidence}"
|
| 199 |
)
|
| 200 |
# 基本边界检查
|
|
@@ -812,7 +812,7 @@ class EnhancedFaceAnalyzer:
|
|
| 812 |
|
| 813 |
# 复制原图用于绘制
|
| 814 |
annotated_image = image.copy()
|
| 815 |
-
logger.
|
| 816 |
f"Input annotated_image shape: {annotated_image.shape}, dtype: {annotated_image.dtype}, ndim: {annotated_image.ndim}"
|
| 817 |
)
|
| 818 |
# 分析每张人脸
|
|
@@ -952,7 +952,7 @@ class EnhancedFaceAnalyzer:
|
|
| 952 |
save_image_force_compress(
|
| 953 |
face_cropped, cropped_face_path, max_size_kb=100
|
| 954 |
)
|
| 955 |
-
logger.
|
| 956 |
except Exception as e:
|
| 957 |
logger.error(f"Failed to save cropped face {cropped_face_path}: {e}")
|
| 958 |
cropped_face_filename = None
|
|
|
|
| 194 |
# 获取边界框坐标 (xyxy格式)
|
| 195 |
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy().astype(int)
|
| 196 |
confidence = float(box.conf[0])
|
| 197 |
+
logger.info(
|
| 198 |
f"detect class_id={class_id}, confidence={confidence}"
|
| 199 |
)
|
| 200 |
# 基本边界检查
|
|
|
|
| 812 |
|
| 813 |
# 复制原图用于绘制
|
| 814 |
annotated_image = image.copy()
|
| 815 |
+
logger.info(
|
| 816 |
f"Input annotated_image shape: {annotated_image.shape}, dtype: {annotated_image.dtype}, ndim: {annotated_image.ndim}"
|
| 817 |
)
|
| 818 |
# 分析每张人脸
|
|
|
|
| 952 |
save_image_force_compress(
|
| 953 |
face_cropped, cropped_face_path, max_size_kb=100
|
| 954 |
)
|
| 955 |
+
logger.info(f"cropped face: {cropped_face_path}")
|
| 956 |
except Exception as e:
|
| 957 |
logger.error(f"Failed to save cropped face {cropped_face_path}: {e}")
|
| 958 |
cropped_face_filename = None
|
facial_analyzer.py
CHANGED
|
@@ -518,7 +518,7 @@ class FacialFeatureAnalyzer:
|
|
| 518 |
基于多个美学比例和对称性指标
|
| 519 |
"""
|
| 520 |
try:
|
| 521 |
-
logger.
|
| 522 |
if len(landmarks) < 68: # 假设使用68点面部关键点
|
| 523 |
return 6.21
|
| 524 |
|
|
@@ -533,37 +533,37 @@ class FacialFeatureAnalyzer:
|
|
| 533 |
|
| 534 |
# 黄金比例评分 (权重: 20%)
|
| 535 |
golden_score = self._calculate_golden_ratios(face_measurements)
|
| 536 |
-
logger.
|
| 537 |
scores.append(("golden_ratio", golden_score, 0.10))
|
| 538 |
|
| 539 |
# 对称性评分 (权重: 25%)
|
| 540 |
symmetry_score = self._calculate_facial_symmetry(face_measurements, points)
|
| 541 |
-
logger.
|
| 542 |
scores.append(("symmetry", symmetry_score, 0.40))
|
| 543 |
|
| 544 |
# 三庭五眼比例 (权重: 20%)
|
| 545 |
proportion_score = self._calculate_classical_proportions(face_measurements)
|
| 546 |
-
logger.
|
| 547 |
scores.append(("proportions", proportion_score, 0.05))
|
| 548 |
|
| 549 |
# 五官间距协调性 (权重: 15%)
|
| 550 |
spacing_score = self._calculate_feature_spacing(face_measurements)
|
| 551 |
-
logger.
|
| 552 |
scores.append(("spacing", spacing_score, 0))
|
| 553 |
|
| 554 |
# 面部轮廓协调性 (权重: 10%)
|
| 555 |
contour_score = self._calculate_contour_harmony(points)
|
| 556 |
-
logger.
|
| 557 |
scores.append(("contour", contour_score, 0.05))
|
| 558 |
|
| 559 |
# 眼鼻口比例协调性 (权重: 10%)
|
| 560 |
feature_score = self._calculate_feature_proportions(face_measurements)
|
| 561 |
-
logger.
|
| 562 |
scores.append(("features", feature_score, 0.40))
|
| 563 |
|
| 564 |
# 加权平均计算最终得分
|
| 565 |
final_score = sum(score * weight for _, score, weight in scores)
|
| 566 |
-
logger.
|
| 567 |
return max(0, min(10, final_score))
|
| 568 |
|
| 569 |
except Exception as e:
|
|
@@ -879,7 +879,7 @@ class FacialFeatureAnalyzer:
|
|
| 879 |
try:
|
| 880 |
# 复制原图用于绘制
|
| 881 |
annotated_image = face_image.copy()
|
| 882 |
-
|
| 883 |
# MediaPipe需要RGB图像
|
| 884 |
rgb_image = cv2.cvtColor(face_image, cv2.COLOR_BGR2RGB)
|
| 885 |
|
|
@@ -900,7 +900,7 @@ class FacialFeatureAnalyzer:
|
|
| 900 |
y = int(landmark.y * h)
|
| 901 |
# 绘制小圆点表示关键点
|
| 902 |
cv2.circle(annotated_image, (x, y), 1, (0, 255, 0), -1)
|
| 903 |
-
|
| 904 |
# 绘制十字标记
|
| 905 |
cv2.line(annotated_image, (x-2, y), (x+2, y), (0, 255, 0), 1)
|
| 906 |
cv2.line(annotated_image, (x, y-2), (x, y+2), (0, 255, 0), 1)
|
|
|
|
| 518 |
基于多个美学比例和对称性指标
|
| 519 |
"""
|
| 520 |
try:
|
| 521 |
+
logger.info(f"face landmarks={len(landmarks)}")
|
| 522 |
if len(landmarks) < 68: # 假设使用68点面部关键点
|
| 523 |
return 6.21
|
| 524 |
|
|
|
|
| 533 |
|
| 534 |
# 黄金比例评分 (权重: 20%)
|
| 535 |
golden_score = self._calculate_golden_ratios(face_measurements)
|
| 536 |
+
logger.info(f"Golden ratio score={golden_score}")
|
| 537 |
scores.append(("golden_ratio", golden_score, 0.10))
|
| 538 |
|
| 539 |
# 对称性评分 (权重: 25%)
|
| 540 |
symmetry_score = self._calculate_facial_symmetry(face_measurements, points)
|
| 541 |
+
logger.info(f"Symmetry score={symmetry_score}")
|
| 542 |
scores.append(("symmetry", symmetry_score, 0.40))
|
| 543 |
|
| 544 |
# 三庭五眼比例 (权重: 20%)
|
| 545 |
proportion_score = self._calculate_classical_proportions(face_measurements)
|
| 546 |
+
logger.info(f"Three courts five eyes ratio={proportion_score}")
|
| 547 |
scores.append(("proportions", proportion_score, 0.05))
|
| 548 |
|
| 549 |
# 五官间距协调性 (权重: 15%)
|
| 550 |
spacing_score = self._calculate_feature_spacing(face_measurements)
|
| 551 |
+
logger.info(f"Facial feature spacing harmony={spacing_score}")
|
| 552 |
scores.append(("spacing", spacing_score, 0))
|
| 553 |
|
| 554 |
# 面部轮廓协调性 (权重: 10%)
|
| 555 |
contour_score = self._calculate_contour_harmony(points)
|
| 556 |
+
logger.info(f"Facial contour harmony={contour_score}")
|
| 557 |
scores.append(("contour", contour_score, 0.05))
|
| 558 |
|
| 559 |
# 眼鼻口比例协调性 (权重: 10%)
|
| 560 |
feature_score = self._calculate_feature_proportions(face_measurements)
|
| 561 |
+
logger.info(f"Eye-nose-mouth proportion harmony={feature_score}")
|
| 562 |
scores.append(("features", feature_score, 0.40))
|
| 563 |
|
| 564 |
# 加权平均计算最终得分
|
| 565 |
final_score = sum(score * weight for _, score, weight in scores)
|
| 566 |
+
logger.info(f"Weighted average final score={final_score}")
|
| 567 |
return max(0, min(10, final_score))
|
| 568 |
|
| 569 |
except Exception as e:
|
|
|
|
| 879 |
try:
|
| 880 |
# 复制原图用于绘制
|
| 881 |
annotated_image = face_image.copy()
|
| 882 |
+
|
| 883 |
# MediaPipe需要RGB图像
|
| 884 |
rgb_image = cv2.cvtColor(face_image, cv2.COLOR_BGR2RGB)
|
| 885 |
|
|
|
|
| 900 |
y = int(landmark.y * h)
|
| 901 |
# 绘制小圆点表示关键点
|
| 902 |
cv2.circle(annotated_image, (x, y), 1, (0, 255, 0), -1)
|
| 903 |
+
|
| 904 |
# 绘制十字标记
|
| 905 |
cv2.line(annotated_image, (x-2, y), (x+2, y), (0, 255, 0), 1)
|
| 906 |
cv2.line(annotated_image, (x, y-2), (x, y+2), (0, 255, 0), 1)
|
utils.py
CHANGED
|
@@ -609,7 +609,7 @@ def save_image_force_compress(
|
|
| 609 |
if len(encoded_img) <= max_bytes:
|
| 610 |
with open(output_path, "wb") as f:
|
| 611 |
f.write(encoded_img)
|
| 612 |
-
logger.
|
| 613 |
f"压缩后图像大小: {len(encoded_img) / 1024:.2f} KB,scale={scale:.2f}, quality={quality}"
|
| 614 |
)
|
| 615 |
upload_file_to_bos(output_path)
|
|
@@ -646,7 +646,7 @@ def move_file_to_archive(file_path: str):
|
|
| 646 |
filename = os.path.basename(file_path)
|
| 647 |
destination = os.path.join(IMAGES_DIR, filename)
|
| 648 |
shutil.move(file_path, destination)
|
| 649 |
-
logger.
|
| 650 |
except Exception as error:
|
| 651 |
logger.error(f"Failed to move file {file_path} to archive: {error}")
|
| 652 |
|
|
|
|
| 609 |
if len(encoded_img) <= max_bytes:
|
| 610 |
with open(output_path, "wb") as f:
|
| 611 |
f.write(encoded_img)
|
| 612 |
+
logger.info(
|
| 613 |
f"压缩后图像大小: {len(encoded_img) / 1024:.2f} KB,scale={scale:.2f}, quality={quality}"
|
| 614 |
)
|
| 615 |
upload_file_to_bos(output_path)
|
|
|
|
| 646 |
filename = os.path.basename(file_path)
|
| 647 |
destination = os.path.join(IMAGES_DIR, filename)
|
| 648 |
shutil.move(file_path, destination)
|
| 649 |
+
logger.info(f"Moved file to archive: {destination}")
|
| 650 |
except Exception as error:
|
| 651 |
logger.error(f"Failed to move file {file_path} to archive: {error}")
|
| 652 |
|
vector_store.py
CHANGED
|
@@ -102,7 +102,7 @@ def save_index():
|
|
| 102 |
faiss.write_index(index, FAISS_INDEX_PATH)
|
| 103 |
with open(ID_MAP_PATH, "wb") as f:
|
| 104 |
pickle.dump(id_map, f)
|
| 105 |
-
logger.
|
| 106 |
except Exception as e:
|
| 107 |
logger.error(f"Failed to save vector index: {e}")
|
| 108 |
|
|
|
|
| 102 |
faiss.write_index(index, FAISS_INDEX_PATH)
|
| 103 |
with open(ID_MAP_PATH, "wb") as f:
|
| 104 |
pickle.dump(id_map, f)
|
| 105 |
+
logger.info("Vector index saved")
|
| 106 |
except Exception as e:
|
| 107 |
logger.error(f"Failed to save vector index: {e}")
|
| 108 |
|
wx_access_token.py
CHANGED
|
@@ -33,7 +33,7 @@ async def get_access_token() -> Optional[str]:
|
|
| 33 |
time.time() + result.get("expires_in", 7200) - 300
|
| 34 |
)
|
| 35 |
expires_time = access_token_cache["expires_at"]
|
| 36 |
-
logger.
|
| 37 |
f"成功获取 stable access_token expires_time={expires_time}"
|
| 38 |
)
|
| 39 |
return result["access_token"]
|
|
|
|
| 33 |
time.time() + result.get("expires_in", 7200) - 300
|
| 34 |
)
|
| 35 |
expires_time = access_token_cache["expires_at"]
|
| 36 |
+
logger.info(
|
| 37 |
f"成功获取 stable access_token expires_time={expires_time}"
|
| 38 |
)
|
| 39 |
return result["access_token"]
|