fix(GPU): 修复GFPGAN增强后蓝色遮罩问题
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 9s
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m4s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 2m55s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Failing after 3m16s
CI/CD Pipeline / Validate - Security (pull_request) Failing after 3m18s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m15s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 5m28s
AI Code Review / AI Code Review (pull_request) Successful in 8m8s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled

根因:return_rgb=False 返回 BGR 格式,但后续 cvtColor 误用 RGB2BGR
导致 R/B 通道互换,人脸变蓝。

修复:
- return_rgb 改为 True,输出为 RGB 格式
- 保持后续 RGB→BGR 转换正确
- 简化逻辑:去掉复杂的色彩校正(mean/std对齐),
  避免引入额外的色偏风险
- 添加清晰的色彩通道注释,防止后续维护出错
This commit is contained in:
saas-backend
2026-09-22 12:53:45 +08:00
parent 746899964b
commit 130120c8a7
+12 -17
View File
@@ -919,35 +919,30 @@ def _run_inference(
_ff_proc.stdin.write(ori_frame.tobytes())
continue
# GFPGAN 人脸超分增强(FP32 + 色彩校正,避免 FP16 色偏)
# GFPGAN 人脸超分增强(FP32 推理,避免 FP16 色偏)
# 色彩通道约定:ori_frame / res_frame / _face_up 均为 BGR(OpenCV 默认);
# GFPGAN 输出用 return_rgb=True 拿到 RGB,再转 BGR,与后续 face_parsing 融合保持一致。
if gfpgan_enhancer is not None:
try:
_fh, _fw = res_frame_resized.shape[:2]
_face_up = cv2.resize(res_frame_resized, (512, 512),
interpolation=cv2.INTER_LANCZOS4)
# 保存原始人脸区域用于色彩校正
_face_original_bgr = _face_up.copy()
_face_rgb = cv2.cvtColor(_face_up, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
_face_t = torch.from_numpy(_face_rgb.transpose(2,0,1)).unsqueeze(0)
_face_t = torch.from_numpy(_face_rgb.transpose(2, 0, 1)).unsqueeze(0)
# GFPGAN 始终 FP32,避免 FP16 精度导致色偏;归一化到 [-1, 1]
_face_t = ((_face_t - 0.5) / 0.5).to(device)
with torch.no_grad():
_out = gfpgan_enhancer(_face_t, return_rgb=False, weight=0.35)[0]
_out = _out.squeeze(0).float().cpu().clamp_(-1,1)
_out = ((_out + 1)/2*255).numpy().transpose(1,2,0)
_out_bgr = cv2.cvtColor(_out.astype(np.uint8), cv2.COLOR_RGB2BGR)
# 色彩校正:将增强结果的均值/标准差对齐到原始人脸,消除色调偏移
_orig_mean = _face_original_bgr.mean(axis=(0, 1))
_orig_std = _face_original_bgr.std(axis=(0, 1)) + 1e-6
_enh_mean = _out_bgr.mean(axis=(0, 1))
_enh_std = _out_bgr.std(axis=(0, 1)) + 1e-6
_out_bgr = ((_out_bgr.astype(np.float32) - _enh_mean) * (_orig_std / _enh_std) + _orig_mean)
_out_bgr = np.clip(_out_bgr, 0, 255).astype(np.uint8)
_out = gfpgan_enhancer(_face_t, return_rgb=True, weight=0.35)[0]
# 输出 tensor: RGB, [-1, 1] 范围 → clamp → 映射到 [0, 255] uint8
_out = _out.squeeze(0).float().cpu().clamp_(-1.0, 1.0)
_out = ((_out + 1.0) / 2.0 * 255.0).numpy().transpose(1, 2, 0)
_out_rgb = _out.astype(np.uint8)
# RGB → BGR,与 ori_frame 保持一致,确保 face_parsing 融合时通道正确
_out_bgr = cv2.cvtColor(_out_rgb, cv2.COLOR_RGB2BGR)
res_frame_resized = cv2.resize(_out_bgr, (_fw, _fh),
interpolation=cv2.INTER_LANCZOS4)
del _face_t, _out, _out_bgr, _face_original_bgr
del _face_t, _out, _out_rgb, _out_bgr
except Exception as _gfpgan_err:
logger.warning("GFPGAN 增强失败(帧 %d),使用原图: %s", i, _gfpgan_err)