chenchaoyun
commited on
Commit
·
5dd6d61
1
Parent(s):
71f998e
fix
Browse files- api_routes.py +85 -185
- utils.py +10 -2
api_routes.py
CHANGED
|
@@ -12,7 +12,7 @@ import uuid
|
|
| 12 |
import subprocess
|
| 13 |
from concurrent.futures import ThreadPoolExecutor
|
| 14 |
from datetime import datetime
|
| 15 |
-
from typing import Any, Dict, List, Optional
|
| 16 |
|
| 17 |
import cv2
|
| 18 |
import numpy as np
|
|
@@ -4186,32 +4186,24 @@ async def face_similarity_verification(
|
|
| 4186 |
if not face_boxes2:
|
| 4187 |
raise HTTPException(status_code=400, detail="第二张图片中未检测到人脸,请上传包含清晰人脸的图片")
|
| 4188 |
|
| 4189 |
-
# 保存原始图片到IMAGES_DIR
|
| 4190 |
original_path1 = os.path.join(IMAGES_DIR, original_filename1)
|
| 4191 |
-
if save_image_high_quality(
|
| 4192 |
-
|
| 4193 |
-
|
| 4194 |
-
|
| 4195 |
-
|
| 4196 |
-
|
| 4197 |
-
|
| 4198 |
-
"source": "face_verify",
|
| 4199 |
-
"role": "original1",
|
| 4200 |
-
},
|
| 4201 |
-
)
|
| 4202 |
|
| 4203 |
original_path2 = os.path.join(IMAGES_DIR, original_filename2)
|
| 4204 |
-
if save_image_high_quality(
|
| 4205 |
-
|
| 4206 |
-
|
| 4207 |
-
|
| 4208 |
-
|
| 4209 |
-
|
| 4210 |
-
|
| 4211 |
-
"source": "face_verify",
|
| 4212 |
-
"role": "original2",
|
| 4213 |
-
},
|
| 4214 |
-
)
|
| 4215 |
|
| 4216 |
# 调用DeepFace.verify进行人脸比对
|
| 4217 |
logger.info("Starting DeepFace verification...")
|
|
@@ -4294,66 +4286,57 @@ async def face_similarity_verification(
|
|
| 4294 |
if analyzer is None:
|
| 4295 |
_ensure_analyzer()
|
| 4296 |
|
| 4297 |
-
|
| 4298 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4299 |
try:
|
| 4300 |
-
|
| 4301 |
-
|
| 4302 |
-
|
| 4303 |
-
|
| 4304 |
-
|
| 4305 |
-
|
| 4306 |
-
|
| 4307 |
-
|
| 4308 |
-
|
| 4309 |
-
|
| 4310 |
-
|
| 4311 |
-
|
| 4312 |
-
|
| 4313 |
-
|
| 4314 |
-
|
| 4315 |
-
|
| 4316 |
-
|
| 4317 |
-
|
| 4318 |
-
|
| 4319 |
-
|
| 4320 |
-
|
| 4321 |
-
|
| 4322 |
-
|
| 4323 |
-
|
| 4324 |
-
|
| 4325 |
-
|
| 4326 |
-
|
| 4327 |
-
|
| 4328 |
-
|
| 4329 |
-
|
| 4330 |
-
|
| 4331 |
-
|
| 4332 |
-
|
| 4333 |
-
|
| 4334 |
-
|
| 4335 |
-
|
| 4336 |
-
|
| 4337 |
-
|
| 4338 |
-
|
| 4339 |
-
|
| 4340 |
-
|
| 4341 |
-
|
| 4342 |
-
|
| 4343 |
-
if save_image_high_quality(original_with_landmarks_img2,
|
| 4344 |
-
original_path2,
|
| 4345 |
-
quality=SAVE_QUALITY):
|
| 4346 |
-
await _record_output_file(
|
| 4347 |
-
file_path=original_path2,
|
| 4348 |
-
nickname=nickname,
|
| 4349 |
-
category="original",
|
| 4350 |
-
extra={
|
| 4351 |
-
"source": "face_verify",
|
| 4352 |
-
"role": "original2_landmarks",
|
| 4353 |
-
},
|
| 4354 |
-
)
|
| 4355 |
-
except Exception as e:
|
| 4356 |
-
logger.warning(f"Failed to draw facial landmarks on original images: {e}")
|
| 4357 |
# 如果有区域信息,则裁剪人脸
|
| 4358 |
if img1_region and img2_region:
|
| 4359 |
try:
|
|
@@ -4371,126 +4354,43 @@ async def face_similarity_verification(
|
|
| 4371 |
face_img1 = image1[y1:y1_end, x1:x1_end]
|
| 4372 |
face_img2 = image2[y2:y2_end, x2:x2_end]
|
| 4373 |
|
| 4374 |
-
# 保存裁剪后的人脸图片
|
| 4375 |
face_path1 = os.path.join(IMAGES_DIR, face_filename1)
|
| 4376 |
face_path2 = os.path.join(IMAGES_DIR, face_filename2)
|
| 4377 |
-
|
| 4378 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4379 |
await _record_output_file(
|
| 4380 |
file_path=face_path1,
|
| 4381 |
nickname=nickname,
|
| 4382 |
category="face",
|
| 4383 |
extra={
|
| 4384 |
"source": "face_verify",
|
| 4385 |
-
"role": "face1",
|
|
|
|
| 4386 |
},
|
| 4387 |
)
|
| 4388 |
-
if save_image_high_quality(
|
| 4389 |
-
quality=SAVE_QUALITY):
|
| 4390 |
await _record_output_file(
|
| 4391 |
file_path=face_path2,
|
| 4392 |
nickname=nickname,
|
| 4393 |
category="face",
|
| 4394 |
extra={
|
| 4395 |
"source": "face_verify",
|
| 4396 |
-
"role": "face2",
|
|
|
|
| 4397 |
},
|
| 4398 |
)
|
| 4399 |
-
|
| 4400 |
-
# 确保分析器已初始化,用于绘制特征点
|
| 4401 |
-
if analyzer is None:
|
| 4402 |
-
_ensure_analyzer()
|
| 4403 |
-
|
| 4404 |
-
# 在人脸图像上绘制特征点(如果分析器可用)
|
| 4405 |
-
if analyzer is not None:
|
| 4406 |
-
try:
|
| 4407 |
-
face_with_landmarks_img1 = analyzer.facial_analyzer.draw_facial_landmarks(face_img1)
|
| 4408 |
-
face_with_landmarks_img2 = analyzer.facial_analyzer.draw_facial_landmarks(face_img2)
|
| 4409 |
-
|
| 4410 |
-
# 保存带特征点的人脸图片(使用原来的文件名)
|
| 4411 |
-
face_with_landmarks_path1 = os.path.join(IMAGES_DIR, face_filename1)
|
| 4412 |
-
face_with_landmarks_path2 = os.path.join(IMAGES_DIR, face_filename2)
|
| 4413 |
-
if save_image_high_quality(face_with_landmarks_img1,
|
| 4414 |
-
face_with_landmarks_path1,
|
| 4415 |
-
quality=SAVE_QUALITY):
|
| 4416 |
-
await _record_output_file(
|
| 4417 |
-
file_path=face_with_landmarks_path1,
|
| 4418 |
-
nickname=nickname,
|
| 4419 |
-
category="face",
|
| 4420 |
-
extra={
|
| 4421 |
-
"source": "face_verify",
|
| 4422 |
-
"role": "face1_landmarks",
|
| 4423 |
-
},
|
| 4424 |
-
)
|
| 4425 |
-
if save_image_high_quality(face_with_landmarks_img2,
|
| 4426 |
-
face_with_landmarks_path2,
|
| 4427 |
-
quality=SAVE_QUALITY):
|
| 4428 |
-
await _record_output_file(
|
| 4429 |
-
file_path=face_with_landmarks_path2,
|
| 4430 |
-
nickname=nickname,
|
| 4431 |
-
category="face",
|
| 4432 |
-
extra={
|
| 4433 |
-
"source": "face_verify",
|
| 4434 |
-
"role": "face2_landmarks",
|
| 4435 |
-
},
|
| 4436 |
-
)
|
| 4437 |
-
except Exception as e:
|
| 4438 |
-
logger.warning(f"Failed to draw facial landmarks: {e}")
|
| 4439 |
-
# 如果绘制特征点失败,使用裁剪的人脸图像
|
| 4440 |
-
face_with_landmarks_path1 = os.path.join(IMAGES_DIR, face_filename1)
|
| 4441 |
-
face_with_landmarks_path2 = os.path.join(IMAGES_DIR, face_filename2)
|
| 4442 |
-
if save_image_high_quality(face_img1,
|
| 4443 |
-
face_with_landmarks_path1,
|
| 4444 |
-
quality=SAVE_QUALITY):
|
| 4445 |
-
await _record_output_file(
|
| 4446 |
-
file_path=face_with_landmarks_path1,
|
| 4447 |
-
nickname=nickname,
|
| 4448 |
-
category="face",
|
| 4449 |
-
extra={
|
| 4450 |
-
"source": "face_verify",
|
| 4451 |
-
"role": "face1_landmarks_fallback",
|
| 4452 |
-
},
|
| 4453 |
-
)
|
| 4454 |
-
if save_image_high_quality(face_img2,
|
| 4455 |
-
face_with_landmarks_path2,
|
| 4456 |
-
quality=SAVE_QUALITY):
|
| 4457 |
-
await _record_output_file(
|
| 4458 |
-
file_path=face_with_landmarks_path2,
|
| 4459 |
-
nickname=nickname,
|
| 4460 |
-
category="face",
|
| 4461 |
-
extra={
|
| 4462 |
-
"source": "face_verify",
|
| 4463 |
-
"role": "face2_landmarks_fallback",
|
| 4464 |
-
},
|
| 4465 |
-
)
|
| 4466 |
-
else:
|
| 4467 |
-
# 如果分析器不可用,使用裁剪的人脸图像
|
| 4468 |
-
face_with_landmarks_path1 = os.path.join(IMAGES_DIR, face_filename1)
|
| 4469 |
-
face_with_landmarks_path2 = os.path.join(IMAGES_DIR, face_filename2)
|
| 4470 |
-
if save_image_high_quality(face_img1,
|
| 4471 |
-
face_with_landmarks_path1,
|
| 4472 |
-
quality=SAVE_QUALITY):
|
| 4473 |
-
await _record_output_file(
|
| 4474 |
-
file_path=face_with_landmarks_path1,
|
| 4475 |
-
nickname=nickname,
|
| 4476 |
-
category="face",
|
| 4477 |
-
extra={
|
| 4478 |
-
"source": "face_verify",
|
| 4479 |
-
"role": "face1_no_landmarks",
|
| 4480 |
-
},
|
| 4481 |
-
)
|
| 4482 |
-
if save_image_high_quality(face_img2,
|
| 4483 |
-
face_with_landmarks_path2,
|
| 4484 |
-
quality=SAVE_QUALITY):
|
| 4485 |
-
await _record_output_file(
|
| 4486 |
-
file_path=face_with_landmarks_path2,
|
| 4487 |
-
nickname=nickname,
|
| 4488 |
-
category="face",
|
| 4489 |
-
extra={
|
| 4490 |
-
"source": "face_verify",
|
| 4491 |
-
"role": "face2_no_landmarks",
|
| 4492 |
-
},
|
| 4493 |
-
)
|
| 4494 |
except Exception as e:
|
| 4495 |
logger.warning(f"Failed to crop faces: {e}")
|
| 4496 |
else:
|
|
|
|
| 12 |
import subprocess
|
| 13 |
from concurrent.futures import ThreadPoolExecutor
|
| 14 |
from datetime import datetime
|
| 15 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 16 |
|
| 17 |
import cv2
|
| 18 |
import numpy as np
|
|
|
|
| 4186 |
if not face_boxes2:
|
| 4187 |
raise HTTPException(status_code=400, detail="第二张图片中未检测到人脸,请上传包含清晰人脸的图片")
|
| 4188 |
|
| 4189 |
+
# 保存原始图片到IMAGES_DIR(先不上传 BOS,供 DeepFace 使用)
|
| 4190 |
original_path1 = os.path.join(IMAGES_DIR, original_filename1)
|
| 4191 |
+
if not save_image_high_quality(
|
| 4192 |
+
image1,
|
| 4193 |
+
original_path1,
|
| 4194 |
+
quality=SAVE_QUALITY,
|
| 4195 |
+
upload_to_bos=False,
|
| 4196 |
+
):
|
| 4197 |
+
raise HTTPException(status_code=500, detail="保存第一张原始图片失败")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4198 |
|
| 4199 |
original_path2 = os.path.join(IMAGES_DIR, original_filename2)
|
| 4200 |
+
if not save_image_high_quality(
|
| 4201 |
+
image2,
|
| 4202 |
+
original_path2,
|
| 4203 |
+
quality=SAVE_QUALITY,
|
| 4204 |
+
upload_to_bos=False,
|
| 4205 |
+
):
|
| 4206 |
+
raise HTTPException(status_code=500, detail="保存第二张原始图片失败")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4207 |
|
| 4208 |
# 调用DeepFace.verify进行人脸比对
|
| 4209 |
logger.info("Starting DeepFace verification...")
|
|
|
|
| 4286 |
if analyzer is None:
|
| 4287 |
_ensure_analyzer()
|
| 4288 |
|
| 4289 |
+
def _apply_landmarks_on_original(
|
| 4290 |
+
source_image: np.ndarray,
|
| 4291 |
+
region: dict,
|
| 4292 |
+
label: str,
|
| 4293 |
+
) -> Tuple[np.ndarray, bool]:
|
| 4294 |
+
if analyzer is None or not region:
|
| 4295 |
+
return source_image, False
|
| 4296 |
try:
|
| 4297 |
+
x = max(0, region.get("x", 0))
|
| 4298 |
+
y = max(0, region.get("y", 0))
|
| 4299 |
+
w = region.get("w", 0)
|
| 4300 |
+
h = region.get("h", 0)
|
| 4301 |
+
x_end = min(source_image.shape[1], x + w)
|
| 4302 |
+
y_end = min(source_image.shape[0], y + h)
|
| 4303 |
+
if x_end <= x or y_end <= y:
|
| 4304 |
+
return source_image, False
|
| 4305 |
+
result_img = source_image.copy()
|
| 4306 |
+
face_region = result_img[y:y_end, x:x_end]
|
| 4307 |
+
face_with_landmarks = analyzer.facial_analyzer.draw_facial_landmarks(face_region)
|
| 4308 |
+
result_img[y:y_end, x:x_end] = face_with_landmarks
|
| 4309 |
+
return result_img, True
|
| 4310 |
+
except Exception as exc:
|
| 4311 |
+
logger.warning(f"Failed to draw facial landmarks on original image {label}: {exc}")
|
| 4312 |
+
return source_image, False
|
| 4313 |
+
|
| 4314 |
+
original_output_img1, original1_has_landmarks = _apply_landmarks_on_original(image1, img1_region, "1")
|
| 4315 |
+
original_output_img2, original2_has_landmarks = _apply_landmarks_on_original(image2, img2_region, "2")
|
| 4316 |
+
|
| 4317 |
+
if save_image_high_quality(original_output_img1, original_path1, quality=SAVE_QUALITY):
|
| 4318 |
+
await _record_output_file(
|
| 4319 |
+
file_path=original_path1,
|
| 4320 |
+
nickname=nickname,
|
| 4321 |
+
category="original",
|
| 4322 |
+
extra={
|
| 4323 |
+
"source": "face_verify",
|
| 4324 |
+
"role": "original1_landmarks" if original1_has_landmarks else "original1",
|
| 4325 |
+
"with_landmarks": original1_has_landmarks,
|
| 4326 |
+
},
|
| 4327 |
+
)
|
| 4328 |
+
if save_image_high_quality(original_output_img2, original_path2, quality=SAVE_QUALITY):
|
| 4329 |
+
await _record_output_file(
|
| 4330 |
+
file_path=original_path2,
|
| 4331 |
+
nickname=nickname,
|
| 4332 |
+
category="original",
|
| 4333 |
+
extra={
|
| 4334 |
+
"source": "face_verify",
|
| 4335 |
+
"role": "original2_landmarks" if original2_has_landmarks else "original2",
|
| 4336 |
+
"with_landmarks": original2_has_landmarks,
|
| 4337 |
+
},
|
| 4338 |
+
)
|
| 4339 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4340 |
# 如果有区域信息,则裁剪人脸
|
| 4341 |
if img1_region and img2_region:
|
| 4342 |
try:
|
|
|
|
| 4354 |
face_img1 = image1[y1:y1_end, x1:x1_end]
|
| 4355 |
face_img2 = image2[y2:y2_end, x2:x2_end]
|
| 4356 |
|
|
|
|
| 4357 |
face_path1 = os.path.join(IMAGES_DIR, face_filename1)
|
| 4358 |
face_path2 = os.path.join(IMAGES_DIR, face_filename2)
|
| 4359 |
+
# 根据分析器可用性决定是否绘制特征点,仅保存最终版本一次
|
| 4360 |
+
def _prepare_face_image(face_img, face_index):
|
| 4361 |
+
if analyzer is None:
|
| 4362 |
+
return face_img, False
|
| 4363 |
+
try:
|
| 4364 |
+
return analyzer.facial_analyzer.draw_facial_landmarks(face_img.copy()), True
|
| 4365 |
+
except Exception as exc:
|
| 4366 |
+
logger.warning(f"Failed to draw facial landmarks on face{face_index}: {exc}")
|
| 4367 |
+
return face_img, False
|
| 4368 |
+
|
| 4369 |
+
face_output_img1, face1_has_landmarks = _prepare_face_image(face_img1, 1)
|
| 4370 |
+
face_output_img2, face2_has_landmarks = _prepare_face_image(face_img2, 2)
|
| 4371 |
+
|
| 4372 |
+
if save_image_high_quality(face_output_img1, face_path1, quality=SAVE_QUALITY):
|
| 4373 |
await _record_output_file(
|
| 4374 |
file_path=face_path1,
|
| 4375 |
nickname=nickname,
|
| 4376 |
category="face",
|
| 4377 |
extra={
|
| 4378 |
"source": "face_verify",
|
| 4379 |
+
"role": "face1_landmarks" if face1_has_landmarks else "face1",
|
| 4380 |
+
"with_landmarks": face1_has_landmarks,
|
| 4381 |
},
|
| 4382 |
)
|
| 4383 |
+
if save_image_high_quality(face_output_img2, face_path2, quality=SAVE_QUALITY):
|
|
|
|
| 4384 |
await _record_output_file(
|
| 4385 |
file_path=face_path2,
|
| 4386 |
nickname=nickname,
|
| 4387 |
category="face",
|
| 4388 |
extra={
|
| 4389 |
"source": "face_verify",
|
| 4390 |
+
"role": "face2_landmarks" if face2_has_landmarks else "face2",
|
| 4391 |
+
"with_landmarks": face2_has_landmarks,
|
| 4392 |
},
|
| 4393 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4394 |
except Exception as e:
|
| 4395 |
logger.warning(f"Failed to crop faces: {e}")
|
| 4396 |
else:
|
utils.py
CHANGED
|
@@ -706,12 +706,19 @@ def move_file_to_archive(file_path: str):
|
|
| 706 |
logger.error(f"Failed to move file {file_path} to archive: {error}")
|
| 707 |
|
| 708 |
|
| 709 |
-
def save_image_high_quality(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 710 |
"""
|
| 711 |
保存图像,保持高质量,不进行压缩
|
| 712 |
:param image: 图像数组
|
| 713 |
:param output_path: 输出路径
|
| 714 |
:param quality: WebP质量 (0-100),默认95
|
|
|
|
| 715 |
:return: 保存是否成功
|
| 716 |
"""
|
| 717 |
try:
|
|
@@ -726,7 +733,8 @@ def save_image_high_quality(image: np.ndarray, output_path: str, quality: int =
|
|
| 726 |
f.write(encoded_img)
|
| 727 |
|
| 728 |
logger.info(f"High quality image saved successfully: {output_path}, quality: {quality}, size: {len(encoded_img) / 1024:.2f} KB")
|
| 729 |
-
|
|
|
|
| 730 |
return True
|
| 731 |
except Exception as e:
|
| 732 |
logger.error(f"Failed to save image: {output_path}, error: {e}")
|
|
|
|
| 706 |
logger.error(f"Failed to move file {file_path} to archive: {error}")
|
| 707 |
|
| 708 |
|
| 709 |
+
def save_image_high_quality(
|
| 710 |
+
image: np.ndarray,
|
| 711 |
+
output_path: str,
|
| 712 |
+
quality: int = SAVE_QUALITY,
|
| 713 |
+
*,
|
| 714 |
+
upload_to_bos: bool = True,
|
| 715 |
+
) -> bool:
|
| 716 |
"""
|
| 717 |
保存图像,保持高质量,不进行压缩
|
| 718 |
:param image: 图像数组
|
| 719 |
:param output_path: 输出路径
|
| 720 |
:param quality: WebP质量 (0-100),默认95
|
| 721 |
+
:param upload_to_bos: 是否在写入后同步至 BOS
|
| 722 |
:return: 保存是否成功
|
| 723 |
"""
|
| 724 |
try:
|
|
|
|
| 733 |
f.write(encoded_img)
|
| 734 |
|
| 735 |
logger.info(f"High quality image saved successfully: {output_path}, quality: {quality}, size: {len(encoded_img) / 1024:.2f} KB")
|
| 736 |
+
if upload_to_bos:
|
| 737 |
+
upload_file_to_bos(output_path)
|
| 738 |
return True
|
| 739 |
except Exception as e:
|
| 740 |
logger.error(f"Failed to save image: {output_path}, error: {e}")
|