浏览代码

update face api

deason 11 月之前
父节点
当前提交
874a34c940

+ 75 - 26
examcloud-starters/examcloud-face-verify-starter/src/main/java/cn/com/qmth/examcloud/starters/face/verify/model/baidu/BaiduApiHelper.java

@@ -19,6 +19,8 @@ public class BaiduApiHelper {
 
     private final static Logger log = LoggerFactory.getLogger(BaiduApiHelper.class);
 
+    private static double expectFaceLivenessScore = 0.39d;
+
     public static BaiduSession getAccessToken(String apiKey, String apiSecret) {
         if (StringUtils.isBlank(apiKey)) {
             throw new IllegalArgumentException("[BAIDU] apiKey must be not empty.");
@@ -62,7 +64,7 @@ public class BaiduApiHelper {
         RequestBody formBody = FormBody.create(MediaType.parse(Constants.JSON_CONTENT_TYPE), jsonParams);
 
         BaiduResponse response = callApi(requestUrl, formBody);
-        return parseFaceDetectResult(response);
+        return parseFaceVerifyResult(response);
     }
 
     public static FaceResult faceVerifyUsePrivateApi(FaceVerifyProperties properties, String jsonParams) {
@@ -74,7 +76,7 @@ public class BaiduApiHelper {
         RequestBody formBody = FormBody.create(MediaType.parse(Constants.JSON_CONTENT_TYPE), jsonParams);
 
         BaiduResponse response = callApi(requestUrl, formBody);
-        return parseFaceDetectResult(response);
+        return parseFaceVerifyResult(response);
     }
 
     public static FaceResult faceDetect(FaceVerifyProperties properties, String jsonParams) {
@@ -131,6 +133,58 @@ public class BaiduApiHelper {
         return parseFaceCompareResult(response, expectFaceCompareScore);
     }
 
+    private static FaceResult parseFaceVerifyResult(BaiduResponse response) {
+        FaceResult result = new FaceResult();
+        result.setFacePass(false);
+        result.setFaceScore(0d);
+        result.setFaceNum(0);
+        result.setJsonResult(new JsonHelper().style(Include.NON_NULL).toJson(response));
+
+        int errorCode = response.getError_code();
+        if (errorCode != 0) {
+            result.setApiNeedRetry(true);// 默认值
+            result.setError(errorCode + " - " + response.getError_msg());
+
+            if (errorCode == 222202 || errorCode == 222203 || errorCode == 222209 || errorCode == 222304) {
+                // 222202 图片中没有人脸
+                // 222203 无法解析人脸
+                // 222209 face token不存在
+                // 222304 图片尺寸太大
+
+                result.setApiNeedRetry(false);
+            }
+
+            // 请求并发超限(约20QPS,可按需调整)
+            if (errorCode == 18) {
+                log.debug("baidu api qps limit...");
+            }
+
+            return result;
+        }
+
+        BaiduResult data = response.getResult();
+        if (data == null) {
+            result.setError(result.getJsonResult());
+            return result;
+        }
+
+        double faceLiveness = data.getFace_liveness() != null ? data.getFace_liveness() : 0d;
+        result.setFaceScore(faceLiveness);
+
+        // 注:该接口的faceNum值没多大参考意义!照片中包含多张人脸时,默认取人脸面积最大的人脸,返回face_list的元素数量总不会大于1。
+        int faceNum = data.getFace_list() != null ? data.getFace_list().size() : 0;
+        result.setFaceNum(faceNum);
+
+        // 活体分数值,推荐阈值0.393241
+        if (faceNum == 1 && faceLiveness >= expectFaceLivenessScore) {
+            result.setFacePass(true);
+        } else {
+            result.setError("人脸数量:" + faceNum + "; 真实性得分:" + faceLiveness + ",阈值:" + expectFaceLivenessScore);
+        }
+
+        return result;
+    }
+
     private static FaceResult parseFaceDetectResult(BaiduResponse response) {
         FaceResult result = new FaceResult();
         result.setFacePass(false);
@@ -152,10 +206,10 @@ public class BaiduApiHelper {
                 result.setApiNeedRetry(false);
             }
 
-            // 请求并发超限(约10个/秒,可按需调整)
-            /*if (errorCode == 18) {
-                log.warn("facepp_api_limit...");
-            }*/
+            // 请求并发超限(约20QPS,可按需调整)
+            if (errorCode == 18) {
+                log.debug("baidu api qps limit...");
+            }
 
             return result;
         }
@@ -168,29 +222,23 @@ public class BaiduApiHelper {
 
         double faceLiveness = 0d;
         List<BaiduFace> faces = data.getFace_list();
-        if (data.getFace_liveness() != null) {
-            faceLiveness = data.getFace_liveness();
-        } else {
-            if (faces != null && !faces.isEmpty()) {
-                BaiduFace face = faces.get(0);
-                if (face.getLiveness() != null && face.getLiveness().getLivemapscore() != null) {
-                    faceLiveness = face.getLiveness().getLivemapscore();
-                }
+        if (faces != null && !faces.isEmpty()) {
+            BaiduFace face = faces.get(0);
+            if (face.getLiveness() != null && face.getLiveness().getLivemapscore() != null) {
+                faceLiveness = face.getLiveness().getLivemapscore();
             }
         }
         result.setFaceScore(faceLiveness);
 
-        if (data.getFace_num() != null) {
-            result.setFaceNum(data.getFace_num());
-        } else {
-            result.setFaceNum(faces != null ? faces.size() : 0);
-        }
+        // 注:照片中包含多张人脸时,优先取人脸面积最大的人脸,返回的face_list的最大数量由接口请求参数“max_face_num(默认值1)”的值限定。
+        int faceNum = faces != null ? faces.size() : 0;
+        result.setFaceNum(faceNum);
 
         // 活体分数值,推荐阈值0.393241
-        if (faceLiveness >= 0.39d) {
+        if (faceNum == 1 && faceLiveness >= expectFaceLivenessScore) {
             result.setFacePass(true);
         } else {
-            result.setError("faceLiveness=" + faceLiveness + " but less than 0.39");
+            result.setError("人脸数量:" + faceNum + "; 人脸得分:" + faceLiveness + ",阈值:" + expectFaceLivenessScore);
         }
 
         return result;
@@ -221,10 +269,10 @@ public class BaiduApiHelper {
                 result.setApiNeedRetry(false);
             }
 
-            // 请求并发超限(约10个/秒,可按需调整)
-            /*if (errorCode == 18) {
-                log.warn("facepp_api_limit...");
-            }*/
+            // 请求并发超限(约20QPS,可按需调整)
+            if (errorCode == 18) {
+                log.debug("baidu api qps limit...");
+            }
 
             return result;
         }
@@ -238,10 +286,11 @@ public class BaiduApiHelper {
         double score = data.getScore() != null ? data.getScore() : 0d;
         result.setFaceScore(score);
 
+        // 人脸相似度得分,推荐阈值80
         if (score >= expectFaceCompareScore) {
             result.setFacePass(true);
         } else {
-            result.setError("faceScore=" + score + " but less than " + expectFaceCompareScore);
+            result.setError("人脸相似度得分:" + score + ",阈值:" + expectFaceCompareScore);
         }
 
         return result;

+ 10 - 10
examcloud-starters/examcloud-face-verify-starter/src/main/java/cn/com/qmth/examcloud/starters/face/verify/model/faceplus/FacePlusApiHelper.java

@@ -63,10 +63,10 @@ public class FacePlusApiHelper {
                 result.setApiNeedRetry(false);
             }
 
-            // 请求并发超限(约20个/秒,可按需调整)
-            /*if (errorMessage.startsWith("CONCURRENCY_LIMIT_EXCEEDED")) {
-                log.warn("facepp_api_limit...");
-            }*/
+            // 请求并发超限(约20QPS,可按需调整)
+            if (errorMessage.startsWith("CONCURRENCY_LIMIT_EXCEEDED")) {
+                log.debug("face++ api qps limit...");
+            }
 
             return result;
         }
@@ -78,7 +78,7 @@ public class FacePlusApiHelper {
             // 是否人脸检测通过 (只有一张人脸算成功,否则算失败)
             result.setFacePass(true);
         } else {
-            result.setError("faceNum=" + faceNum + " but notEq 1");
+            result.setError("人脸数量:" + faceNum + ",限定一张人脸!");
         }
 
         return result;
@@ -125,10 +125,10 @@ public class FacePlusApiHelper {
                 result.setApiNeedRetry(false);
             }
 
-            // 请求并发超限(约20个/秒,可按需调整)
-            /*if (errorMessage.startsWith("CONCURRENCY_LIMIT_EXCEEDED")) {
-                log.warn("facepp_api_limit...");
-            }*/
+            // 请求并发超限(约20QPS,可按需调整)
+            if (errorMessage.startsWith("CONCURRENCY_LIMIT_EXCEEDED")) {
+                log.debug("face++ api qps limit...");
+            }
 
             return result;
         }
@@ -142,7 +142,7 @@ public class FacePlusApiHelper {
             if (confidence >= thresholds.getLe_4()) {
                 result.setFacePass(true);
             } else {
-                result.setError("faceConfidence=" + confidence + " but less than " + thresholds.getLe_4());
+                result.setError("人脸相似度得分:" + confidence + ",置信度阈值1e-4:" + thresholds.getLe_4());
             }
 
             if (response.getFaces2() != null) {

+ 2 - 1
examcloud-starters/examcloud-face-verify-starter/src/main/java/cn/com/qmth/examcloud/starters/face/verify/service/impl/FaceVerifyServiceImpl.java

@@ -46,7 +46,7 @@ public class FaceVerifyServiceImpl implements FaceVerifyService {
                 FaceApiType.PRIVATE_BAIDU_API == faceParam.getApiType();
 
         Map<String, String> imageData = this.buildImageParamForBaidu(faceParam.getImages()[0], usePrivateBaiduApi);
-        imageData.put("face_field", "spoofing");
+        imageData.put("face_field", "spoofing,face_type,gender,age");
         List<Map<String, String>> images = new ArrayList<>();
         images.add(imageData);
         String params = new JsonHelper().toJson(images);
@@ -78,6 +78,7 @@ public class FaceVerifyServiceImpl implements FaceVerifyService {
                 FaceApiType.PRIVATE_BAIDU_API == faceParam.getApiType();
 
         Map<String, String> imageData = this.buildImageParamForBaidu(faceParam.getImages()[0], usePrivateBaiduApi);
+        imageData.put("face_field", "spoofing,face_type,gender,age");
         imageData.put("liveness_control", "NORMAL");
         imageData.put("max_face_num", "2");
         String params = new JsonHelper().toJson(imageData);