chenchaoyun commited on
Commit
5dd6d61
·
1 Parent(s): 71f998e
Files changed (2) hide show
  1. api_routes.py +85 -185
  2. 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(image1, original_path1,
4192
- quality=SAVE_QUALITY):
4193
- await _record_output_file(
4194
- file_path=original_path1,
4195
- nickname=nickname,
4196
- category="original",
4197
- extra={
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(image2, original_path2,
4205
- quality=SAVE_QUALITY):
4206
- await _record_output_file(
4207
- file_path=original_path2,
4208
- nickname=nickname,
4209
- category="original",
4210
- extra={
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
- if analyzer is not None:
 
 
 
 
 
4299
  try:
4300
- # 在第一张原始图像上绘制特征点
4301
- original_with_landmarks_img1 = image1.copy()
4302
- if img1_region:
4303
- # 提取人脸区域坐标
4304
- x1, y1, w1, h1 = img1_region.get("x", 0), img1_region.get("y", 0), img1_region.get("w", 0), img1_region.get("h", 0)
4305
- # 确保坐标在图像范围内
4306
- x1, y1 = max(0, x1), max(0, y1)
4307
- x1_end, y1_end = min(image1.shape[1], x1 + w1), min(image1.shape[0], y1 + h1)
4308
- # 裁剪人脸区域
4309
- face_region1 = image1[y1:y1_end, x1:x1_end]
4310
- # 在人脸区域上绘制特征点
4311
- face_with_landmarks1 = analyzer.facial_analyzer.draw_facial_landmarks(face_region1)
4312
- # 将绘制了特征点的人脸区域放回原图
4313
- original_with_landmarks_img1[y1:y1_end, x1:x1_end] = face_with_landmarks1
4314
-
4315
- # 在第二张原始图像上绘制特征点
4316
- original_with_landmarks_img2 = image2.copy()
4317
- if img2_region:
4318
- # 提取人脸区域坐标
4319
- x2, y2, w2, h2 = img2_region.get("x", 0), img2_region.get("y", 0), img2_region.get("w", 0), img2_region.get("h", 0)
4320
- # 确保坐标在图像范围内
4321
- x2, y2 = max(0, x2), max(0, y2)
4322
- x2_end, y2_end = min(image2.shape[1], x2 + w2), min(image2.shape[0], y2 + h2)
4323
- # 裁剪人脸区域
4324
- face_region2 = image2[y2:y2_end, x2:x2_end]
4325
- # 在人脸区域上绘制特征点
4326
- face_with_landmarks2 = analyzer.facial_analyzer.draw_facial_landmarks(face_region2)
4327
- # 将绘制了特征点的人脸区域放回原图
4328
- original_with_landmarks_img2[y2:y2_end, x2:x2_end] = face_with_landmarks2
4329
-
4330
- # 保存带特征点的原始图片
4331
- if save_image_high_quality(original_with_landmarks_img1,
4332
- original_path1,
4333
- quality=SAVE_QUALITY):
4334
- await _record_output_file(
4335
- file_path=original_path1,
4336
- nickname=nickname,
4337
- category="original",
4338
- extra={
4339
- "source": "face_verify",
4340
- "role": "original1_landmarks",
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
- if save_image_high_quality(face_img1, face_path1,
4378
- quality=SAVE_QUALITY):
 
 
 
 
 
 
 
 
 
 
 
 
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(face_img2, face_path2,
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(image: np.ndarray, output_path: str, quality: int = SAVE_QUALITY) -> bool:
 
 
 
 
 
 
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
- upload_file_to_bos(output_path)
 
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}")