修改人脸识别算法
parent
71d85458a4
commit
ee10019aa1
8
pom.xml
8
pom.xml
|
@ -46,7 +46,6 @@
|
|||
<lowagie.iTextAsian.version>1.0</lowagie.iTextAsian.version>
|
||||
<itextpdf.version>5.5.13</itextpdf.version>
|
||||
<aspose.words.version>15.8.0</aspose.words.version>
|
||||
<opencv.version>4.9.0-0</opencv.version>
|
||||
</properties>
|
||||
|
||||
<!-- 依赖声明 -->
|
||||
|
@ -296,13 +295,6 @@
|
|||
<version>8.0.31</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OpenCV -->
|
||||
<dependency>
|
||||
<groupId>org.openpnp</groupId>
|
||||
<artifactId>opencv</artifactId>
|
||||
<version>${opencv.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
|
|
@ -154,11 +154,19 @@
|
|||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- OpenCV -->
|
||||
<!-- 百度人脸识别SDK -->
|
||||
<dependency>
|
||||
<groupId>org.openpnp</groupId>
|
||||
<artifactId>opencv</artifactId>
|
||||
<groupId>com.baidu.aip</groupId>
|
||||
<artifactId>java-sdk</artifactId>
|
||||
<version>4.16.14</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON处理 -->
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20230227</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
</project>
|
|
@ -0,0 +1,276 @@
|
|||
package com.yanzhu.common.core.utils;
|
||||
|
||||
import com.baidu.aip.face.AipFace;
|
||||
import com.baidu.aip.face.MatchRequest;
|
||||
import org.json.JSONObject;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.*;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
/**
|
||||
* 基于百度人脸识别API的人脸相似度对比工具类
|
||||
*
|
||||
* @author yanzhu
|
||||
*/
|
||||
public class BaiduFaceSimilarityUtils {
|
||||
|
||||
// 百度人脸识别应用的API Key和Secret Key
|
||||
private static final String APP_ID = "您的AppID"; // 需要设置有效的AppID
|
||||
private static final String API_KEY = "L3PiEzO9dMFJDExMTjOxuTtq";
|
||||
private static final String SECRET_KEY = "40LMey6WC1MYIqLU4m5Qe8K4foFUM1bc";
|
||||
|
||||
// 初始化AipFace客户端
|
||||
private static final AipFace client = new AipFace(APP_ID, API_KEY, SECRET_KEY);
|
||||
|
||||
// 相似度阈值,大于此值认为是同一个人
|
||||
private static final double SIMILARITY_THRESHOLD = 0.8;
|
||||
|
||||
// QPS限制相关常量
|
||||
private static final int QPS_LIMIT_DELAY = 1000; // 1秒延迟,避免QPS限制
|
||||
private static final int MAX_RETRY_ATTEMPTS = 3; // 最大重试次数
|
||||
private static final int RETRY_DELAY = 2000; // 重试延迟(毫秒)
|
||||
|
||||
static {
|
||||
// 设置网络连接参数
|
||||
client.setConnectionTimeoutInMillis(2000);
|
||||
client.setSocketTimeoutInMillis(60000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两张人脸图片的相似度
|
||||
*
|
||||
* @param imageUrl1 第一张图片URL
|
||||
* @param imageUrl2 第二张图片URL
|
||||
* @return 相似度,范围0-1,越大表示越相似
|
||||
*/
|
||||
public static double calculateFaceSimilarity(String imageUrl1, String imageUrl2) {
|
||||
try {
|
||||
// 下载图片并转换为Base64编码
|
||||
String imageBase64_1 = downloadAndEncodeImage(imageUrl1);
|
||||
String imageBase64_2 = downloadAndEncodeImage(imageUrl2);
|
||||
|
||||
if (imageBase64_1 == null || imageBase64_2 == null) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// 调用百度人脸识别API进行人脸对比
|
||||
return calculateImageSimilarityByBaiduAPI(imageBase64_1, imageBase64_2);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两张Base64编码人脸图片的相似度
|
||||
*
|
||||
* @param imageBase64_1 第一张图片的Base64编码
|
||||
* @param imageBase64_2 第二张图片的Base64编码
|
||||
* @return 相似度,范围0-1,越大表示越相似
|
||||
*/
|
||||
public static double calculateFaceSimilarityByBase64(String imageBase64_1, String imageBase64_2) {
|
||||
try {
|
||||
// 调用百度人脸识别API进行人脸对比
|
||||
return calculateImageSimilarityByBaiduAPI(imageBase64_1, imageBase64_2);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两张人脸图片是否匹配(用于考勤等场景)
|
||||
*
|
||||
* @param imageUrl1 第一张图片URL
|
||||
* @param imageUrl2 第二张图片URL
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean isFaceMatchForAttendance(String imageUrl1, String imageUrl2) {
|
||||
double similarity = calculateFaceSimilarity(imageUrl1, imageUrl2);
|
||||
return similarity >= SIMILARITY_THRESHOLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断两张Base64编码人脸图片是否匹配(用于考勤等场景)
|
||||
*
|
||||
* @param imageBase64_1 第一张图片的Base64编码
|
||||
* @param imageBase64_2 第二张图片的Base64编码
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean isFaceMatchForAttendanceByBase64(String imageBase64_1, String imageBase64_2) {
|
||||
double similarity = calculateFaceSimilarityByBase64(imageBase64_1, imageBase64_2);
|
||||
return similarity >= SIMILARITY_THRESHOLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载图片并转换为Base64编码
|
||||
*
|
||||
* @param imageUrl 图片URL
|
||||
* @return Base64编码的图片字符串
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
private static String downloadAndEncodeImage(String imageUrl) throws IOException {
|
||||
try {
|
||||
URL url = new URL(imageUrl);
|
||||
URLConnection connection = url.openConnection();
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
|
||||
BufferedImage image = ImageIO.read(connection.getInputStream());
|
||||
if (image == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 将图片转换为Base64编码
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "jpg", baos);
|
||||
byte[] imageBytes = baos.toByteArray();
|
||||
return Base64.getEncoder().encodeToString(imageBytes);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用百度人脸识别API进行人脸对比
|
||||
*
|
||||
* @param imageBase64_1 第一张图片的Base64编码
|
||||
* @param imageBase64_2 第二张图片的Base64编码
|
||||
* @return 相似度,范围0-1
|
||||
*/
|
||||
private static double calculateImageSimilarityByBaiduAPI(String imageBase64_1, String imageBase64_2) {
|
||||
return calculateImageSimilarityByBaiduAPIWithRetry(imageBase64_1, imageBase64_2, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 带重试机制的百度人脸识别API调用
|
||||
*
|
||||
* @param imageBase64_1 第一张图片的Base64编码
|
||||
* @param imageBase64_2 第二张图片的Base64编码
|
||||
* @param attempt 当前尝试次数
|
||||
* @return 相似度,范围0-1
|
||||
*/
|
||||
private static double calculateImageSimilarityByBaiduAPIWithRetry(String imageBase64_1, String imageBase64_2, int attempt) {
|
||||
try {
|
||||
// 添加延迟以避免QPS限制
|
||||
if (attempt > 0) {
|
||||
Thread.sleep(RETRY_DELAY);
|
||||
} else {
|
||||
Thread.sleep(QPS_LIMIT_DELAY);
|
||||
}
|
||||
|
||||
// 创建MatchRequest列表
|
||||
List<MatchRequest> requests = new ArrayList<>();
|
||||
MatchRequest req1 = new MatchRequest(imageBase64_1, "BASE64");
|
||||
req1.setFaceType("LIVE");
|
||||
req1.setQualityControl("LOW");
|
||||
req1.setLivenessControl("NONE");
|
||||
|
||||
MatchRequest req2 = new MatchRequest(imageBase64_2, "BASE64");
|
||||
req2.setFaceType("LIVE");
|
||||
req2.setQualityControl("LOW");
|
||||
req2.setLivenessControl("NONE");
|
||||
|
||||
requests.add(req1);
|
||||
requests.add(req2);
|
||||
|
||||
// 调用百度人脸识别API的人脸对比接口
|
||||
JSONObject response = client.match(requests);
|
||||
|
||||
// 解析返回结果
|
||||
if (response != null) {
|
||||
// 检查是否有QPS限制错误
|
||||
if (response.has("error_code") && response.get("error_code") != null) {
|
||||
String errorCode = response.get("error_code").toString();
|
||||
if ("18".equals(errorCode)) { // 18是QPS限制错误码
|
||||
System.err.println("百度API QPS限制: Open api qps request limit reached");
|
||||
if (attempt < MAX_RETRY_ATTEMPTS) {
|
||||
System.out.println("第" + (attempt + 1) + "次重试...");
|
||||
return calculateImageSimilarityByBaiduAPIWithRetry(imageBase64_1, imageBase64_2, attempt + 1);
|
||||
} else {
|
||||
System.err.println("达到最大重试次数,无法完成请求");
|
||||
return 0.0;
|
||||
}
|
||||
} else if (!errorCode.equals("0")) {
|
||||
System.err.println("百度API错误: " + response.optString("error_msg", "未知错误"));
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查result字段是否存在且不为null
|
||||
if (response.has("result") && !response.isNull("result")) {
|
||||
JSONObject result = response.getJSONObject("result");
|
||||
if (result != null && result.has("score")) {
|
||||
// 百度API返回的相似度是百分比,需要转换为0-1范围
|
||||
double score = result.getDouble("score");
|
||||
return score / 100.0;
|
||||
}
|
||||
} else {
|
||||
// 如果result为null,输出详细信息用于调试
|
||||
System.err.println("百度API返回结果为空或无效: " + response.toString());
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置API Key和Secret Key
|
||||
*
|
||||
* @param appId App ID
|
||||
* @param apiKey API Key
|
||||
* @param secretKey Secret Key
|
||||
*/
|
||||
public static void setCredentials(String appId, String apiKey, String secretKey) {
|
||||
// 重新初始化AipFace客户端
|
||||
// 注意:在实际应用中,应该通过配置文件或环境变量来设置这些密钥
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("开始测试人脸相似度算法...");
|
||||
|
||||
// 测试1:同一个人的不同照片(应该匹配)
|
||||
String userPicture1 = "https://xiangguan.sxyanzhu.com/statics/2025/09/03/836e5c21f83604894486069394dcd22e_20250903170015A004.jpg";
|
||||
String userPicture2 = "http://62.234.3.186/statics/2025/06/11/a28457e2847333886c8e0f4fd9cd24bc_20250611101313A331.jpg";
|
||||
String userPicture3 = "https://xiangguan.sxyanzhu.com/statics/2025/09/03/c4e2c8fb9d9f66e03902f9e3fea17f99_20250903165717A003.jpg";
|
||||
String userPicture4 = "https://xiangguan.sxyanzhu.com/statics/2025/09/03/8c7dd922ad47494fc02c388e12c00eac_20250903132353A852.png";
|
||||
String userPicture5 = "http://62.234.3.186/statics/2025/06/11/87052f8fa3eaa8840bc2e4fe556a825e_20250611101011A328.jpg";
|
||||
String userPicture6 = "https://xiangguan.sxyanzhu.com/statics/2025/09/05/a851473a6f5f6373b1ea73487d36dfba_20250905095748A594.jpg";
|
||||
// 打印每张图片的人脸信息
|
||||
|
||||
|
||||
Map<String,String[]> map = new HashMap<>();
|
||||
map.put("1-2",new String[]{userPicture1,userPicture2});
|
||||
map.put("1-3",new String[]{userPicture1,userPicture3});
|
||||
map.put("1-4",new String[]{userPicture1,userPicture4});
|
||||
map.put("1-5",new String[]{userPicture1,userPicture5});
|
||||
map.put("2-3",new String[]{userPicture2,userPicture3});
|
||||
map.put("2-4",new String[]{userPicture2,userPicture4});
|
||||
map.put("2-5",new String[]{userPicture2,userPicture5});
|
||||
map.put("3-4",new String[]{userPicture3,userPicture4});
|
||||
map.put("3-5",new String[]{userPicture3,userPicture5});
|
||||
map.put("4-5",new String[]{userPicture4,userPicture5});
|
||||
map.put("3-6",new String[]{userPicture3,userPicture6});
|
||||
System.out.println("相似度测试结果:");
|
||||
for (String key : map.keySet()) {
|
||||
String[] strings = map.get(key);
|
||||
String img1 = strings[0];
|
||||
String img2 = strings[1];
|
||||
double similarity1 = calculateFaceSimilarity(img1, img2);
|
||||
System.out.printf(key+":%f%n",similarity1);
|
||||
}
|
||||
|
||||
System.out.println("\n测试完成。");
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -190,12 +190,6 @@
|
|||
<artifactId>hasor-dataway</artifactId>
|
||||
<version>4.2.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OpenCV -->
|
||||
<dependency>
|
||||
<groupId>org.openpnp</groupId>
|
||||
<artifactId>opencv</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -4,7 +4,7 @@ import java.util.List;
|
|||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.yanzhu.common.core.utils.ImageSimilarityUtils;
|
||||
import com.yanzhu.common.core.utils.BaiduFaceSimilarityUtils;
|
||||
import com.yanzhu.common.core.utils.poi.ExcelUtil;
|
||||
import com.yanzhu.common.core.web.controller.BaseController;
|
||||
import com.yanzhu.common.core.web.domain.AjaxResult;
|
||||
|
@ -133,11 +133,10 @@ public class ProMobileAttendanceConfigController extends BaseController
|
|||
String attImg=attData.getBasePath()+attData.getAttImg();
|
||||
|
||||
// 使用专门为人脸识别考勤优化的相似度计算
|
||||
double similarity = ImageSimilarityUtils.calculateFaceSimilarity(userPicture, attImg);
|
||||
double similarity = BaiduFaceSimilarityUtils.calculateFaceSimilarity(userPicture, attImg);
|
||||
System.out.println("相似度:"+similarity);
|
||||
|
||||
// 根据是否使用OpenCV调整阈值
|
||||
// OpenCV场景下阈值为0.8,基础算法场景下阈值为0.75
|
||||
double threshold = ImageSimilarityUtils.openCVLoaded ? 0.8 : 0.75;
|
||||
double threshold = 0.8;
|
||||
|
||||
if (similarity >= threshold) {
|
||||
// 相似度达标,允许考勤
|
||||
|
|
Loading…
Reference in New Issue