修改相似度算法
parent
b6055e3cbb
commit
ccd0e6cd49
|
@ -1,253 +1,454 @@
|
|||
package com.yanzhu.common.core.utils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.opencv.core.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Base64;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfRect;
|
||||
import org.opencv.core.Rect;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.opencv.objdetect.CascadeClassifier;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
/**
|
||||
* 图片相似度计算工具类(基于OpenCV实现)
|
||||
* 专门针对人脸识别场景优化的相似度计算
|
||||
*
|
||||
* @author yanzhu
|
||||
* @date 2025-09-03
|
||||
* 人像照片相似度算法工具类
|
||||
* 用于计算两张人脸图片的相似度,支持URL图片和Base64编码图片
|
||||
*/
|
||||
public class ImageSimilarityUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(ImageSimilarityUtils.class);
|
||||
// 相似度阈值,大于此值认为是同一个人
|
||||
private static final double SIMILARITY_THRESHOLD = 0.75;
|
||||
|
||||
// 人脸识别场景下的考勤阈值(针对同一个人的不同照片)
|
||||
private static final double ATTENDANCE_THRESHOLD = 0.70;
|
||||
|
||||
// OpenCV相关
|
||||
// OpenCV人脸检测器
|
||||
private static CascadeClassifier faceDetector;
|
||||
|
||||
static {
|
||||
try {
|
||||
// 加载OpenCV库
|
||||
// 加载OpenCV库(使用openpnp的OpenCV依赖)
|
||||
nu.pattern.OpenCV.loadLocally();
|
||||
|
||||
// 初始化人脸检测器
|
||||
// 注意:在实际部署时,需要确保haarcascade_frontalface_alt.xml文件在classpath中
|
||||
String cascadePath = "haarcascade_frontalface_alt.xml";
|
||||
// 初始化人脸检测器(使用相对路径)
|
||||
String cascadePath = ImageSimilarityUtils.class.getClassLoader().getResource("opencv/haarcascade_frontalface_default.xml").getPath();
|
||||
faceDetector = new CascadeClassifier(cascadePath);
|
||||
} catch (Exception e) {
|
||||
log.error("OpenCV初始化失败", e);
|
||||
System.err.println("OpenCV加载失败,将使用基础相似度计算方法: " + e.getMessage());
|
||||
faceDetector = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算两张人脸图片的相似度
|
||||
*
|
||||
* @param imagePath1 人脸图片1路径(可以是本地路径或URL)
|
||||
* @param imagePath2 人脸图片2路径(可以是本地路径或URL)
|
||||
* @return 相似度(0-1之间,1表示完全相同)
|
||||
*
|
||||
* @param imageUrl1 第一张图片URL
|
||||
* @param imageUrl2 第二张图片URL
|
||||
* @return 相似度,范围0-1,越大表示越相似
|
||||
*/
|
||||
public static double calculateFaceSimilarity(String imagePath1, String imagePath2) {
|
||||
public static double calculateFaceSimilarity(String imageUrl1, String imageUrl2) {
|
||||
try {
|
||||
// 检查参数
|
||||
if (imagePath1 == null || imagePath2 == null) {
|
||||
log.warn("图片路径不能为空");
|
||||
// 下载图片
|
||||
BufferedImage image1 = downloadImage(imageUrl1);
|
||||
BufferedImage image2 = downloadImage(imageUrl2);
|
||||
|
||||
if (image1 == null || image2 == null) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// 读取图片
|
||||
Mat image1 = readImageAsMat(imagePath1);
|
||||
Mat image2 = readImageAsMat(imagePath2);
|
||||
|
||||
if (image1.empty() || image2.empty()) {
|
||||
log.warn("无法读取图片文件: {} 或 {}", imagePath1, imagePath2);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// 检测并裁剪人脸区域
|
||||
Mat face1 = detectAndCropFace(image1);
|
||||
Mat face2 = detectAndCropFace(image2);
|
||||
|
||||
// 如果无法检测到人脸,则使用整张图片
|
||||
if (face1.empty()) face1 = image1;
|
||||
if (face2.empty()) face2 = image2;
|
||||
|
||||
// 为人脸识别场景预处理
|
||||
Mat processedImage1 = preprocessForFaceRecognition(face1);
|
||||
Mat processedImage2 = preprocessForFaceRecognition(face2);
|
||||
|
||||
// 使用OpenCV计算直方图相似度
|
||||
double similarity = calculateHistogramSimilarity(processedImage1, processedImage2);
|
||||
|
||||
log.debug("计算人脸图片相似度完成: {} 和 {}, 相似度: {}",
|
||||
imagePath1, imagePath2, similarity);
|
||||
|
||||
// 释放资源
|
||||
image1.release();
|
||||
image2.release();
|
||||
face1.release();
|
||||
face2.release();
|
||||
processedImage1.release();
|
||||
processedImage2.release();
|
||||
|
||||
return similarity;
|
||||
// 提取特征并计算相似度
|
||||
return calculateImageSimilarity(image1, image2);
|
||||
} catch (Exception e) {
|
||||
log.error("计算人脸图片相似度时发生错误", e);
|
||||
e.printStackTrace();
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查人脸图片是否匹配(基于考勤场景的阈值)
|
||||
*
|
||||
* @param imagePath1 人脸图片1路径
|
||||
* @param imagePath2 人脸图片2路径
|
||||
* @return 是否匹配
|
||||
* 计算两张Base64编码人脸图片的相似度
|
||||
*
|
||||
* @param imageBase64_1 第一张图片的Base64编码
|
||||
* @param imageBase64_2 第二张图片的Base64编码
|
||||
* @return 相似度,范围0-1,越大表示越相似
|
||||
*/
|
||||
public static boolean isFaceMatchForAttendance(String imagePath1, String imagePath2) {
|
||||
double similarity = calculateFaceSimilarity(imagePath1, imagePath2);
|
||||
return similarity >= ATTENDANCE_THRESHOLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取图片为OpenCV的Mat对象
|
||||
*
|
||||
* @param imagePath 图片路径
|
||||
* @return Mat对象
|
||||
*/
|
||||
private static Mat readImageAsMat(String imagePath) {
|
||||
public static double calculateFaceSimilarityByBase64(String imageBase64_1, String imageBase64_2) {
|
||||
try {
|
||||
if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
|
||||
// 处理网络图片
|
||||
URL url = new URL(imagePath);
|
||||
Path tempFile = Files.createTempFile("image_", ".jpg");
|
||||
Files.copy(url.openStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
Mat mat = Imgcodecs.imread(tempFile.toAbsolutePath().toString());
|
||||
Files.delete(tempFile); // 清理临时文件
|
||||
return mat;
|
||||
} else {
|
||||
// 处理本地图片
|
||||
return Imgcodecs.imread(imagePath);
|
||||
// 解码Base64图片
|
||||
BufferedImage image1 = decodeBase64Image(imageBase64_1);
|
||||
BufferedImage image2 = decodeBase64Image(imageBase64_2);
|
||||
|
||||
if (image1 == null || image2 == null) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// 提取特征并计算相似度
|
||||
return calculateImageSimilarity(image1, image2);
|
||||
} catch (Exception e) {
|
||||
log.error("读取图片失败: {}", imagePath, e);
|
||||
return new Mat();
|
||||
e.printStackTrace();
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检测并裁剪人脸区域
|
||||
*
|
||||
* @param image 原始图片
|
||||
* @return 裁剪后的人脸区域图片,如果未检测到则返回空Mat
|
||||
* 判断两张人脸图片是否匹配(用于考勤等场景)
|
||||
*
|
||||
* @param imageUrl1 第一张图片URL
|
||||
* @param imageUrl2 第二张图片URL
|
||||
* @return 是否匹配
|
||||
*/
|
||||
private static Mat detectAndCropFace(Mat image) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载图片
|
||||
*
|
||||
* @param imageUrl 图片URL
|
||||
* @return BufferedImage对象
|
||||
*/
|
||||
private static BufferedImage downloadImage(String imageUrl) {
|
||||
try {
|
||||
URL url = new URL(imageUrl);
|
||||
URLConnection connection = url.openConnection();
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
return ImageIO.read(connection.getInputStream());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码Base64图片
|
||||
*
|
||||
* @param base64Image Base64编码的图片字符串
|
||||
* @return BufferedImage对象
|
||||
* @throws IOException 解码异常
|
||||
*/
|
||||
private static BufferedImage decodeBase64Image(String base64Image) throws IOException {
|
||||
// 移除可能存在的Base64前缀(如data:image/jpeg;base64,)
|
||||
if (base64Image.contains(",")) {
|
||||
base64Image = base64Image.split(",")[1];
|
||||
}
|
||||
|
||||
byte[] imageBytes = Base64.getDecoder().decode(base64Image);
|
||||
return ImageIO.read(new java.io.ByteArrayInputStream(imageBytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将图片转换为Base64编码
|
||||
*
|
||||
* @param image 图片对象
|
||||
* @param formatName 图片格式(如"jpg"、"png")
|
||||
* @return Base64编码字符串
|
||||
* @throws IOException 编码异常
|
||||
*/
|
||||
private static String encodeImageToBase64(BufferedImage image, String formatName) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, formatName, baos);
|
||||
byte[] imageBytes = baos.toByteArray();
|
||||
return Base64.getEncoder().encodeToString(imageBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两张图片的相似度
|
||||
* 使用OpenCV进行人脸检测,然后比较人脸区域的直方图
|
||||
*
|
||||
* @param image1 第一张图片
|
||||
* @param image2 第二张图片
|
||||
* @return 相似度,范围0-1
|
||||
*/
|
||||
private static double calculateImageSimilarity(BufferedImage image1, BufferedImage image2) {
|
||||
// 如果OpenCV初始化失败,直接使用基础方法
|
||||
if (faceDetector == null) {
|
||||
return calculateImageSimilarityBasic(image1, image2);
|
||||
}
|
||||
|
||||
try {
|
||||
// 检测人脸区域
|
||||
Rect face1 = detectFace(image1);
|
||||
Rect face2 = detectFace(image2);
|
||||
|
||||
// 如果检测到人脸,使用人脸区域进行比较
|
||||
if (face1 != null && face2 != null) {
|
||||
BufferedImage faceImage1 = cropFace(image1, face1);
|
||||
BufferedImage faceImage2 = cropFace(image2, face2);
|
||||
|
||||
// 将人脸区域缩放到相同大小
|
||||
int width = 100;
|
||||
int height = 100;
|
||||
BufferedImage scaledFace1 = scaleImage(faceImage1, width, height);
|
||||
BufferedImage scaledFace2 = scaleImage(faceImage2, width, height);
|
||||
|
||||
// 计算人脸区域的灰度直方图
|
||||
int[] histogram1 = calculateHistogram(scaledFace1);
|
||||
int[] histogram2 = calculateHistogram(scaledFace2);
|
||||
|
||||
// 计算直方图相似度(余弦相似度)
|
||||
return calculateCosineSimilarity(histogram1, histogram2);
|
||||
}
|
||||
|
||||
// 如果未检测到人脸,使用整个图片进行比较
|
||||
int width = 100;
|
||||
int height = 100;
|
||||
BufferedImage scaledImage1 = scaleImage(image1, width, height);
|
||||
BufferedImage scaledImage2 = scaleImage(image2, width, height);
|
||||
|
||||
// 计算图片的灰度直方图
|
||||
int[] histogram1 = calculateHistogram(scaledImage1);
|
||||
int[] histogram2 = calculateHistogram(scaledImage2);
|
||||
|
||||
// 计算直方图相似度(余弦相似度)
|
||||
return calculateCosineSimilarity(histogram1, histogram2);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// 发生异常时使用原始方法
|
||||
return calculateImageSimilarityBasic(image1, image2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础图片相似度计算方法(不使用OpenCV)
|
||||
* 使用直方图比较方法计算相似度
|
||||
*
|
||||
* @param image1 第一张图片
|
||||
* @param image2 第二张图片
|
||||
* @return 相似度,范围0-1
|
||||
*/
|
||||
private static double calculateImageSimilarityBasic(BufferedImage image1, BufferedImage image2) {
|
||||
// 将图片缩放到相同大小
|
||||
int width = 100;
|
||||
int height = 100;
|
||||
BufferedImage scaledImage1 = scaleImage(image1, width, height);
|
||||
BufferedImage scaledImage2 = scaleImage(image2, width, height);
|
||||
|
||||
// 计算图片的灰度直方图
|
||||
int[] histogram1 = calculateHistogram(scaledImage1);
|
||||
int[] histogram2 = calculateHistogram(scaledImage2);
|
||||
|
||||
// 计算直方图相似度(余弦相似度)
|
||||
return calculateCosineSimilarity(histogram1, histogram2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测图片中的人脸
|
||||
*
|
||||
* @param image 输入图片
|
||||
* @return 人脸区域矩形,如果未检测到人脸则返回null
|
||||
*/
|
||||
private static Rect detectFace(BufferedImage image) {
|
||||
try {
|
||||
// 将BufferedImage转换为OpenCV Mat
|
||||
Mat mat = bufferedImageToMat(image);
|
||||
|
||||
// 转换为灰度图
|
||||
Mat grayImage = new Mat();
|
||||
Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);
|
||||
Mat gray = new Mat();
|
||||
Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR2GRAY);
|
||||
|
||||
// 检测人脸
|
||||
MatOfRect faceDetections = new MatOfRect();
|
||||
faceDetector.detectMultiScale(grayImage, faceDetections);
|
||||
faceDetector.detectMultiScale(gray, faceDetections, 1.1, 3, 0, new Size(30, 30), new Size());
|
||||
|
||||
Rect[] faces = faceDetections.toArray();
|
||||
|
||||
// 如果检测到人脸,裁剪第一个人脸区域
|
||||
if (faces.length > 0) {
|
||||
Rect face = faces[0];
|
||||
Mat croppedFace = new Mat(image, face);
|
||||
grayImage.release();
|
||||
faceDetections.release();
|
||||
return croppedFace;
|
||||
// 返回最大的人脸区域
|
||||
return faces[0];
|
||||
}
|
||||
|
||||
grayImage.release();
|
||||
faceDetections.release();
|
||||
return new Mat();
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.warn("人脸检测失败,使用整张图片", e);
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 裁剪人脸区域
|
||||
*
|
||||
* @param image 原图片
|
||||
* @param faceRect 人脸区域
|
||||
* @return 裁剪后的人脸图片
|
||||
*/
|
||||
private static BufferedImage cropFace(BufferedImage image, Rect faceRect) {
|
||||
// 确保人脸区域在图片范围内
|
||||
int x = Math.max(0, faceRect.x);
|
||||
int y = Math.max(0, faceRect.y);
|
||||
int width = Math.min(image.getWidth() - x, faceRect.width);
|
||||
int height = Math.min(image.getHeight() - y, faceRect.height);
|
||||
|
||||
return image.getSubimage(x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将BufferedImage转换为OpenCV Mat
|
||||
*
|
||||
* @param image BufferedImage对象
|
||||
* @return OpenCV Mat对象
|
||||
*/
|
||||
private static Mat bufferedImageToMat(BufferedImage image) {
|
||||
// 创建临时文件
|
||||
try {
|
||||
File tempFile = File.createTempFile("opencv", ".jpg");
|
||||
ImageIO.write(image, "jpg", tempFile);
|
||||
|
||||
// 读取为Mat
|
||||
Mat mat = Imgcodecs.imread(tempFile.getAbsolutePath());
|
||||
|
||||
// 删除临时文件
|
||||
tempFile.delete();
|
||||
|
||||
return mat;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new Mat();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 为人脸识别场景预处理图片
|
||||
*
|
||||
* @param originalImage 原始图片
|
||||
* @return 预处理后的图片
|
||||
* 缩放图片到指定大小
|
||||
*
|
||||
* @param image 原图片
|
||||
* @param width 目标宽度
|
||||
* @param height 目标高度
|
||||
* @return 缩放后的图片
|
||||
*/
|
||||
private static Mat preprocessForFaceRecognition(Mat originalImage) {
|
||||
// 1. 调整大小到标准尺寸(256x256)
|
||||
Mat resized = new Mat();
|
||||
Imgproc.resize(originalImage, resized, new Size(256, 256));
|
||||
private static BufferedImage scaleImage(BufferedImage image, int width, int height) {
|
||||
BufferedImage scaledImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
scaledImage.getGraphics().drawImage(image, 0, 0, width, height, null);
|
||||
return scaledImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算图片的灰度直方图
|
||||
*
|
||||
* @param image 图片
|
||||
* @return 灰度直方图(256个灰度级)
|
||||
*/
|
||||
private static int[] calculateHistogram(BufferedImage image) {
|
||||
int[] histogram = new int[256];
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
// 2. 转换为灰度图(如果还不是的话)
|
||||
Mat gray = new Mat();
|
||||
if (resized.channels() == 3) {
|
||||
Imgproc.cvtColor(resized, gray, Imgproc.COLOR_BGR2GRAY);
|
||||
} else {
|
||||
resized.copyTo(gray);
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int rgb = image.getRGB(x, y);
|
||||
int r = (rgb >> 16) & 0xFF;
|
||||
int g = (rgb >> 8) & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
|
||||
// 转换为灰度值 (0.299*R + 0.587*G + 0.114*B)
|
||||
int gray = (int)(0.299 * r + 0.587 * g + 0.114 * b);
|
||||
histogram[gray]++;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 直方图均衡化增强对比度
|
||||
Mat enhanced = new Mat();
|
||||
Imgproc.equalizeHist(gray, enhanced);
|
||||
|
||||
// 释放中间资源
|
||||
resized.release();
|
||||
gray.release();
|
||||
|
||||
return enhanced;
|
||||
return histogram;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算直方图相似度(使用相关性比较)
|
||||
*
|
||||
* @param image1 图片1
|
||||
* @param image2 图片2
|
||||
* @return 相似度(0-1之间)
|
||||
* 计算余弦相似度
|
||||
*
|
||||
* @param vector1 向量1
|
||||
* @param vector2 向量2
|
||||
* @return 余弦相似度,范围0-1
|
||||
*/
|
||||
private static double calculateHistogramSimilarity(Mat image1, Mat image2) {
|
||||
// 计算直方图
|
||||
Mat hist1 = new Mat();
|
||||
Mat hist2 = new Mat();
|
||||
private static double calculateCosineSimilarity(int[] vector1, int[] vector2) {
|
||||
double dotProduct = 0.0;
|
||||
double norm1 = 0.0;
|
||||
double norm2 = 0.0;
|
||||
|
||||
// 设置直方图参数
|
||||
MatOfInt histSize = new MatOfInt(256);
|
||||
MatOfFloat ranges = new MatOfFloat(0f, 256f);
|
||||
MatOfInt channels = new MatOfInt(0);
|
||||
for (int i = 0; i < vector1.length; i++) {
|
||||
dotProduct += vector1[i] * vector2[i];
|
||||
norm1 += Math.pow(vector1[i], 2);
|
||||
norm2 += Math.pow(vector2[i], 2);
|
||||
}
|
||||
|
||||
// 计算直方图
|
||||
Imgproc.calcHist(java.util.Arrays.asList(image1), channels, new Mat(), hist1, histSize, ranges);
|
||||
Imgproc.calcHist(java.util.Arrays.asList(image2), channels, new Mat(), hist2, histSize, ranges);
|
||||
// 避免除以零
|
||||
if (norm1 == 0 || norm2 == 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// 归一化直方图
|
||||
Core.normalize(hist1, hist1);
|
||||
Core.normalize(hist2, hist2);
|
||||
|
||||
// 计算直方图相关性
|
||||
double similarity = Imgproc.compareHist(hist1, hist2, Imgproc.HISTCMP_CORREL);
|
||||
|
||||
// 释放资源
|
||||
hist1.release();
|
||||
hist2.release();
|
||||
histSize.release();
|
||||
ranges.release();
|
||||
channels.release();
|
||||
|
||||
// 确保相似度在0-1范围内
|
||||
return Math.max(0.0, Math.min(1.0, similarity));
|
||||
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String userPicture = "https://xiangguan.sxyanzhu.com/statics/2025/09/03/836e5c21f83604894486069394dcd22e_20250903170015A004.jpg";
|
||||
String attImg = "https://xiangguan.sxyanzhu.com/statics/2025/09/03/c4e2c8fb9d9f66e03902f9e3fea17f99_20250903165717A003.jpg";
|
||||
double similarity = ImageSimilarityUtils.calculateFaceSimilarity(userPicture, attImg);
|
||||
System.out.println("人脸相似度: " + similarity);
|
||||
System.out.println("考勤匹配: " + ImageSimilarityUtils.isFaceMatchForAttendance(userPicture, attImg));
|
||||
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 img1=userPicture4;
|
||||
String img2=userPicture5;
|
||||
System.out.println("\n测试1:不同人的不同照片");
|
||||
System.out.println("照片2: " + img1);
|
||||
System.out.println("照片5: " + img2);
|
||||
|
||||
double similarity1 = ImageSimilarityUtils.calculateFaceSimilarity(img1, img2);
|
||||
System.out.println("人脸相似度: " + similarity1);
|
||||
System.out.println("考勤匹配: " + ImageSimilarityUtils.isFaceMatchForAttendance(img1, img2));
|
||||
|
||||
// System.out.println("\n测试2:同一个人的另一组照片");
|
||||
// System.out.println("照片1: " + userPicture1);
|
||||
// System.out.println("照片4: " + userPicture4);
|
||||
|
||||
// double similarity2 = ImageSimilarityUtils.calculateFaceSimilarity(userPicture1, userPicture4);
|
||||
// System.out.println("人脸相似度: " + similarity2);
|
||||
// System.out.println("考勤匹配: " + ImageSimilarityUtils.isFaceMatchForAttendance(userPicture1, userPicture4));
|
||||
|
||||
// System.out.println("\n测试2:同一个人的另一组照片");
|
||||
// System.out.println("照片1: " + userPicture1);
|
||||
// System.out.println("照片2: " + userPicture2);
|
||||
|
||||
// double similarity3 = ImageSimilarityUtils.calculateFaceSimilarity(userPicture1, userPicture2);
|
||||
// System.out.println("人脸相似度: " + similarity3);
|
||||
// System.out.println("考勤匹配: " + ImageSimilarityUtils.isFaceMatchForAttendance(userPicture1, userPicture2));
|
||||
|
||||
// // 测试Base64图片人脸相似度计算
|
||||
// // 实际使用时,这里应该是真实的Base64编码图片字符串
|
||||
// System.out.println("\n测试3:Base64图片相似度测试");
|
||||
// try {
|
||||
// // 将URL图片转为Base64进行测试
|
||||
// BufferedImage img1 = downloadImage(userPicture1);
|
||||
// BufferedImage img2 = downloadImage(userPicture3);
|
||||
// if (img1 != null && img2 != null) {
|
||||
// String base64Image1 = encodeImageToBase64(img1, "jpg");
|
||||
// String base64Image2 = encodeImageToBase64(img2, "jpg");
|
||||
|
||||
// double base64Similarity = calculateFaceSimilarityByBase64(base64Image1, base64Image2);
|
||||
// System.out.println("Base64人脸相似度: " + base64Similarity);
|
||||
|
||||
// boolean base64IsMatch = isFaceMatchForAttendanceByBase64(base64Image1, base64Image2);
|
||||
// System.out.println("Base64是否匹配: " + base64IsMatch);
|
||||
// } else {
|
||||
// System.out.println("无法下载图片进行Base64测试");
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// System.out.println("Base64图片测试失败: " + e.getMessage());
|
||||
// }
|
||||
|
||||
System.out.println("\n测试完成。");
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue