修改人脸识别算法

dev_xd
lj7788@126.com 2025-09-05 16:35:30 +08:00
parent 71d85458a4
commit ee10019aa1
7 changed files with 292 additions and 34419 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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 KeySecret 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测试完成。");
}
}

View File

@ -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>

View File

@ -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) {
// 相似度达标,允许考勤