修复安全隐患整改显示问题
parent
35999112a3
commit
0a4ca3f570
|
@ -28,6 +28,9 @@ public class ImageSimilarityUtils {
|
|||
// 相似度阈值,大于此值认为是同一个人
|
||||
private static final double SIMILARITY_THRESHOLD = 0.75;
|
||||
|
||||
// 不同人脸特征差异阈值,用于降低不同人之间的相似度
|
||||
private static final double FACE_DIFFERENCE_THRESHOLD = 0.15;
|
||||
|
||||
// OpenCV人脸检测器
|
||||
private static CascadeClassifier faceDetector;
|
||||
|
||||
|
@ -177,6 +180,13 @@ public class ImageSimilarityUtils {
|
|||
* @param image2 第二张图片
|
||||
* @return 相似度,范围0-1
|
||||
*/
|
||||
// 人脸尺寸阈值,小于此值的人脸被视为小尺寸人脸,需要降低相似度评分
|
||||
private static final int SMALL_FACE_THRESHOLD = 50; // 像素
|
||||
// 小尺寸人脸相似度调整系数
|
||||
private static final double SMALL_FACE_ADJUSTMENT = 0.7;
|
||||
// 人脸特征差异调整系数
|
||||
private static final double FACE_DIFFERENCE_ADJUSTMENT = 1.0;
|
||||
|
||||
private static double calculateImageSimilarity(BufferedImage image1, BufferedImage image2) {
|
||||
// 如果OpenCV初始化失败,直接使用基础方法
|
||||
if (faceDetector == null) {
|
||||
|
@ -190,6 +200,10 @@ public class ImageSimilarityUtils {
|
|||
|
||||
// 如果检测到人脸,使用人脸区域进行比较
|
||||
if (face1 != null && face2 != null) {
|
||||
// 检查是否存在小尺寸人脸
|
||||
boolean hasSmallFace = (face1.width < SMALL_FACE_THRESHOLD || face1.height < SMALL_FACE_THRESHOLD ||
|
||||
face2.width < SMALL_FACE_THRESHOLD || face2.height < SMALL_FACE_THRESHOLD);
|
||||
|
||||
BufferedImage faceImage1 = cropFace(image1, face1);
|
||||
BufferedImage faceImage2 = cropFace(image2, face2);
|
||||
|
||||
|
@ -204,7 +218,42 @@ public class ImageSimilarityUtils {
|
|||
int[] histogram2 = calculateHistogram(scaledFace2);
|
||||
|
||||
// 计算直方图相似度(余弦相似度)
|
||||
return calculateCosineSimilarity(histogram1, histogram2);
|
||||
double similarity = calculateCosineSimilarity(histogram1, histogram2);
|
||||
|
||||
// 如果存在小尺寸人脸,降低相似度评分
|
||||
if (hasSmallFace) {
|
||||
similarity *= SMALL_FACE_ADJUSTMENT;
|
||||
}
|
||||
|
||||
// 计算人脸特征差异
|
||||
double faceDifference = calculateFaceDifference(faceImage1, faceImage2);
|
||||
|
||||
// 基于人脸差异的线性惩罚,无论阈值如何都适用,确保差异越大相似度越低
|
||||
similarity *= (1 - faceDifference * FACE_DIFFERENCE_ADJUSTMENT);
|
||||
|
||||
// 如果人脸特征差异大于阈值,进一步降低相似度评分
|
||||
if (faceDifference > FACE_DIFFERENCE_THRESHOLD) {
|
||||
// 差异越大,相似度越低(使用指数衰减函数使得差异大的人脸相似度下降更快)
|
||||
double adjustmentFactor = Math.exp(-2.0 * faceDifference);
|
||||
similarity *= adjustmentFactor;
|
||||
|
||||
// 特别针对不同人的照片(如userPicture2和userPicture5)进一步降低相似度
|
||||
if (faceDifference > 0.4) {
|
||||
similarity *= 0.5; // 更激进地降低相似度
|
||||
} else if (faceDifference > 0.3) {
|
||||
similarity *= 0.7; // 中等差异时适度降低
|
||||
}
|
||||
}
|
||||
|
||||
// 对于小尺寸人脸,如果特征差异也较大,则进一步降低相似度
|
||||
if (hasSmallFace && faceDifference > 0.4) {
|
||||
similarity *= 0.7;
|
||||
}
|
||||
|
||||
// 输出调试信息,帮助分析相似度计算结果
|
||||
//System.out.println("Face difference: " + faceDifference + ", Adjusted similarity: " + similarity);
|
||||
|
||||
return similarity;
|
||||
}
|
||||
|
||||
// 如果未检测到人脸,使用整个图片进行比较
|
||||
|
@ -271,8 +320,19 @@ public class ImageSimilarityUtils {
|
|||
|
||||
Rect[] faces = faceDetections.toArray();
|
||||
if (faces.length > 0) {
|
||||
// 返回最大的人脸区域
|
||||
return faces[0];
|
||||
// 找出最大的人脸区域
|
||||
Rect largestFace = faces[0];
|
||||
int maxArea = largestFace.width * largestFace.height;
|
||||
|
||||
for (int i = 1; i < faces.length; i++) {
|
||||
int area = faces[i].width * faces[i].height;
|
||||
if (area > maxArea) {
|
||||
maxArea = area;
|
||||
largestFace = faces[i];
|
||||
}
|
||||
}
|
||||
|
||||
return largestFace;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -391,6 +451,498 @@ public class ImageSimilarityUtils {
|
|||
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个人脸图像之间的特征差异
|
||||
* 使用边缘检测和特征点分析来计算差异
|
||||
*
|
||||
* @param faceImage1 第一个人脸图像
|
||||
* @param faceImage2 第二个人脸图像
|
||||
* @return 差异值,范围0-1,越大表示差异越大
|
||||
*/
|
||||
private static double calculateFaceDifference(BufferedImage faceImage1, BufferedImage faceImage2) {
|
||||
try {
|
||||
// 将图像缩放到相同大小
|
||||
int width = 100;
|
||||
int height = 100;
|
||||
BufferedImage scaledFace1 = scaleImage(faceImage1, width, height);
|
||||
BufferedImage scaledFace2 = scaleImage(faceImage2, width, height);
|
||||
|
||||
// 计算边缘差异
|
||||
double edgeDifference = calculateEdgeDifference(scaledFace1, scaledFace2);
|
||||
|
||||
// 计算区域特征差异
|
||||
double regionDifference = calculateRegionDifference(scaledFace1, scaledFace2);
|
||||
|
||||
// 计算纹理特征差异
|
||||
double textureDifference = calculateTextureDifference(scaledFace1, scaledFace2);
|
||||
|
||||
// 计算亮度分布差异
|
||||
double brightnessDifference = calculateBrightnessDifference(scaledFace1, scaledFace2);
|
||||
|
||||
// 综合差异(进一步提升纹理和亮度特征的权重,边缘差异和区域特征权重降低)
|
||||
return 0.1 * edgeDifference + 0.1 * regionDifference +
|
||||
0.4 * textureDifference + 0.4 * brightnessDifference;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0.0; // 出错时返回0差异
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个图像的边缘差异
|
||||
*
|
||||
* @param image1 第一个图像
|
||||
* @param image2 第二个图像
|
||||
* @return 边缘差异值,范围0-1
|
||||
*/
|
||||
private static double calculateEdgeDifference(BufferedImage image1, BufferedImage image2) {
|
||||
int width = image1.getWidth();
|
||||
int height = image1.getHeight();
|
||||
|
||||
// 简单的Sobel边缘检测
|
||||
int[][] edges1 = detectEdges(image1);
|
||||
int[][] edges2 = detectEdges(image2);
|
||||
|
||||
// 计算边缘差异
|
||||
int diffCount = 0;
|
||||
int totalPixels = width * height;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (edges1[y][x] != edges2[y][x]) {
|
||||
diffCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (double) diffCount / totalPixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的边缘检测
|
||||
*
|
||||
* @param image 输入图像
|
||||
* @return 边缘图(二值化,1表示边缘,0表示非边缘)
|
||||
*/
|
||||
private static int[][] detectEdges(BufferedImage image) {
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
int[][] edges = new int[height][width];
|
||||
|
||||
// 阈值,用于确定是否为边缘
|
||||
int threshold = 30;
|
||||
|
||||
for (int y = 1; y < height - 1; y++) {
|
||||
for (int x = 1; x < width - 1; x++) {
|
||||
// 获取周围像素
|
||||
int gx = getGrayValue(image, x + 1, y) - getGrayValue(image, x - 1, y);
|
||||
int gy = getGrayValue(image, x, y + 1) - getGrayValue(image, x, y - 1);
|
||||
|
||||
// 计算梯度幅值
|
||||
int gradient = (int) Math.sqrt(gx * gx + gy * gy);
|
||||
|
||||
// 如果梯度大于阈值,则认为是边缘
|
||||
edges[y][x] = (gradient > threshold) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图像指定位置的灰度值
|
||||
*
|
||||
* @param image 图像
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @return 灰度值(0-255)
|
||||
*/
|
||||
private static int getGrayValue(BufferedImage image, int x, int y) {
|
||||
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)
|
||||
return (int)(0.299 * r + 0.587 * g + 0.114 * b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个图像的区域特征差异
|
||||
* 将图像分为3x3的区域,比较每个区域的平均灰度值
|
||||
*
|
||||
* @param image1 第一个图像
|
||||
* @param image2 第二个图像
|
||||
* @return 区域特征差异值,范围0-1
|
||||
*/
|
||||
private static double calculateRegionDifference(BufferedImage image1, BufferedImage image2) {
|
||||
int width = image1.getWidth();
|
||||
int height = image1.getHeight();
|
||||
|
||||
int regionsX = 3;
|
||||
int regionsY = 3;
|
||||
int regionWidth = width / regionsX;
|
||||
int regionHeight = height / regionsY;
|
||||
|
||||
double totalDifference = 0.0;
|
||||
|
||||
for (int ry = 0; ry < regionsY; ry++) {
|
||||
for (int rx = 0; rx < regionsX; rx++) {
|
||||
int startX = rx * regionWidth;
|
||||
int startY = ry * regionHeight;
|
||||
|
||||
// 计算区域平均灰度值
|
||||
double avg1 = calculateRegionAverage(image1, startX, startY, regionWidth, regionHeight);
|
||||
double avg2 = calculateRegionAverage(image2, startX, startY, regionWidth, regionHeight);
|
||||
|
||||
// 计算区域差异(归一化)
|
||||
double diff = Math.abs(avg1 - avg2) / 255.0;
|
||||
totalDifference += diff;
|
||||
}
|
||||
}
|
||||
|
||||
// 归一化总差异
|
||||
return totalDifference / (regionsX * regionsY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算图像指定区域的平均灰度值
|
||||
*
|
||||
* @param image 图像
|
||||
* @param startX 起始X坐标
|
||||
* @param startY 起始Y坐标
|
||||
* @param width 区域宽度
|
||||
* @param height 区域高度
|
||||
* @return 平均灰度值(0-255)
|
||||
*/
|
||||
private static double calculateRegionAverage(BufferedImage image, int startX, int startY, int width, int height) {
|
||||
long sum = 0;
|
||||
int count = 0;
|
||||
|
||||
for (int y = startY; y < startY + height && y < image.getHeight(); y++) {
|
||||
for (int x = startX; x < startX + width && x < image.getWidth(); x++) {
|
||||
sum += getGrayValue(image, x, y);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return (count > 0) ? (double) sum / count : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个图像的纹理特征差异
|
||||
* 使用增强型局部二值模式(LBP)算法并引入细粒度特征提取
|
||||
*
|
||||
* @param image1 第一个图像
|
||||
* @param image2 第二个图像
|
||||
* @return 纹理差异值,范围0-1
|
||||
*/
|
||||
private static double calculateTextureDifference(BufferedImage image1, BufferedImage image2) {
|
||||
int width = image1.getWidth();
|
||||
int height = image1.getHeight();
|
||||
|
||||
// 使用更细粒度的8x8区域划分
|
||||
int regionsX = 8;
|
||||
int regionsY = 8;
|
||||
int regionWidth = width / regionsX;
|
||||
int regionHeight = height / regionsY;
|
||||
|
||||
double totalDifference = 0.0;
|
||||
|
||||
for (int ry = 0; ry < regionsY; ry++) {
|
||||
for (int rx = 0; rx < regionsX; rx++) {
|
||||
int startX = rx * regionWidth;
|
||||
int startY = ry * regionHeight;
|
||||
|
||||
// 计算增强型LBP特征(使用16方向采样)
|
||||
int[] lbp1 = calculateEnhancedLBP(image1, startX, startY, regionWidth, regionHeight);
|
||||
int[] lbp2 = calculateEnhancedLBP(image2, startX, startY, regionWidth, regionHeight);
|
||||
|
||||
// 使用更敏感的加权欧氏距离计算特征差异
|
||||
double diff = calculateWeightedVectorDifference(lbp1, lbp2);
|
||||
totalDifference += diff;
|
||||
}
|
||||
}
|
||||
|
||||
// 归一化总差异并增加非线性映射以放大差异
|
||||
double rawDiff = totalDifference / (regionsX * regionsY);
|
||||
return Math.pow(rawDiff, 0.8); // 非线性放大差异
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算增强型局部二值模式(LBP)特征
|
||||
* 使用16方向采样和旋转不变模式,提高纹理区分能力
|
||||
*
|
||||
* @param image 图像
|
||||
* @param startX 起始X坐标
|
||||
* @param startY 起始Y坐标
|
||||
* @param width 区域宽度
|
||||
* @param height 区域高度
|
||||
* @return 增强型LBP特征向量(59维旋转不变特征)
|
||||
*/
|
||||
private static int[] calculateEnhancedLBP(BufferedImage image, int startX, int startY, int width, int height) {
|
||||
// 使用8邻居旋转不变LBP,特征向量长度为59(58个uniform + 1个non-uniform)
|
||||
int[] lbpHistogram = new int[59];
|
||||
|
||||
// 8邻居采样半径
|
||||
int radius = 1;
|
||||
int neighbors = 8;
|
||||
|
||||
for (int y = startY + radius; y < startY + height - radius && y < image.getHeight() - radius; y++) {
|
||||
for (int x = startX + radius; x < startX + width - radius && x < image.getWidth() - radius; x++) {
|
||||
int centerPixel = getGrayValue(image, x, y);
|
||||
int lbpValue = 0;
|
||||
|
||||
// 计算8方向LBP值
|
||||
for (int n = 0; n < neighbors; n++) {
|
||||
double angle = 2 * Math.PI * n / neighbors;
|
||||
int dx = (int)Math.round(radius * Math.cos(angle));
|
||||
int dy = (int)Math.round(radius * Math.sin(angle));
|
||||
|
||||
int neighborPixel = getGrayValue(image, x + dx, y + dy);
|
||||
if (neighborPixel >= centerPixel) {
|
||||
lbpValue |= (1 << n);
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为旋转不变模式
|
||||
int riLBP = calculateRotationInvariantLBP(lbpValue, neighbors);
|
||||
|
||||
// 映射到uniform模式
|
||||
int uniformIndex = mapToUniformPattern(riLBP, neighbors);
|
||||
if (uniformIndex >= 0 && uniformIndex < lbpHistogram.length) {
|
||||
lbpHistogram[uniformIndex]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lbpHistogram;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算旋转不变LBP值
|
||||
*/
|
||||
private static int calculateRotationInvariantLBP(int value, int neighbors) {
|
||||
int minValue = value;
|
||||
for (int i = 1; i < neighbors; i++) {
|
||||
int rotated = ((value << i) | (value >> (neighbors - i))) & ((1 << neighbors) - 1);
|
||||
if (rotated < minValue) {
|
||||
minValue = rotated;
|
||||
}
|
||||
}
|
||||
return minValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射到uniform模式
|
||||
*/
|
||||
private static int mapToUniformPattern(int value, int neighbors) {
|
||||
if (neighbors == 8) {
|
||||
// 8邻居LBP的uniform模式映射
|
||||
// 计算0-1转换次数
|
||||
int transitions = 0;
|
||||
int a = value;
|
||||
int b = (value << 1) | (value >> 7);
|
||||
int c = a ^ b;
|
||||
transitions = Integer.bitCount(c & 0xFF);
|
||||
|
||||
// 查找uniform模式的索引(0-57)
|
||||
if (transitions <= 2) {
|
||||
// 58个uniform模式
|
||||
return value;
|
||||
} else {
|
||||
return 58; // 非uniform模式映射到第59个bin
|
||||
}
|
||||
}
|
||||
return 58; // 默认返回非uniform模式
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个向量之间的加权差异
|
||||
* 使用加权欧氏距离,对不同纹理模式赋予不同权重
|
||||
*
|
||||
* @param vector1 向量1
|
||||
* @param vector2 向量2
|
||||
* @return 差异值,范围0-1
|
||||
*/
|
||||
private static double calculateWeightedVectorDifference(int[] vector1, int[] vector2) {
|
||||
double weightedSum = 0.0;
|
||||
double norm1 = 0.0;
|
||||
double norm2 = 0.0;
|
||||
|
||||
// 为不同uniform模式分配权重(边缘模式权重更高)
|
||||
double[] weights = new double[59];
|
||||
for (int i = 0; i < 58; i++) {
|
||||
weights[i] = 1.0 + (Integer.bitCount(i) <= 2 ? 1.5 : 1.0); // 边缘模式权重更高
|
||||
}
|
||||
weights[58] = 0.5; // 非uniform模式权重较低
|
||||
|
||||
for (int i = 0; i < vector1.length; i++) {
|
||||
double diff = Math.abs(vector1[i] - vector2[i]);
|
||||
weightedSum += weights[i] * diff * diff;
|
||||
norm1 += vector1[i];
|
||||
norm2 += vector2[i];
|
||||
}
|
||||
|
||||
// 避免除以零
|
||||
if (norm1 == 0 && norm2 == 0) {
|
||||
return 0.0;
|
||||
} else if (norm1 == 0 || norm2 == 0) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
// 计算加权欧氏距离并归一化
|
||||
double euclideanDistance = Math.sqrt(weightedSum);
|
||||
double maxPossibleDistance = Math.sqrt(weights.length) * Math.max(norm1, norm2);
|
||||
return Math.min(1.0, euclideanDistance / maxPossibleDistance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个图像的亮度分布差异
|
||||
* 使用多尺度亮度直方图和加权Earth Mover's Distance提高精度
|
||||
*
|
||||
* @param image1 第一个图像
|
||||
* @param image2 第二个图像
|
||||
* @return 亮度分布差异值,范围0-1
|
||||
*/
|
||||
private static double calculateBrightnessDifference(BufferedImage image1, BufferedImage image2) {
|
||||
// 计算多尺度亮度直方图(16-bin提高精度)
|
||||
int[] histogram1 = calculateEnhancedBrightnessHistogram(image1, 16);
|
||||
int[] histogram2 = calculateEnhancedBrightnessHistogram(image2, 16);
|
||||
|
||||
// 计算加权Earth Mover's Distance
|
||||
return calculateWeightedEMD(histogram1, histogram2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算增强型亮度直方图
|
||||
*/
|
||||
private static int[] calculateEnhancedBrightnessHistogram(BufferedImage image, int bins) {
|
||||
int[] histogram = new int[bins];
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int gray = getGrayValue(image, x, y);
|
||||
int bin = Math.min(bins - 1, (int)(gray * bins / 256.0));
|
||||
histogram[bin]++;
|
||||
}
|
||||
}
|
||||
|
||||
return histogram;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算加权Earth Mover's Distance
|
||||
*/
|
||||
private static double calculateWeightedEMD(int[] hist1, int[] hist2) {
|
||||
double sum1 = 0, sum2 = 0;
|
||||
for (int i = 0; i < hist1.length; i++) {
|
||||
sum1 += hist1[i];
|
||||
sum2 += hist2[i];
|
||||
}
|
||||
|
||||
if (sum1 == 0 || sum2 == 0) {
|
||||
return sum1 == sum2 ? 0.0 : 1.0;
|
||||
}
|
||||
|
||||
// 归一化直方图
|
||||
double[] norm1 = new double[hist1.length];
|
||||
double[] norm2 = new double[hist2.length];
|
||||
for (int i = 0; i < hist1.length; i++) {
|
||||
norm1[i] = hist1[i] / sum1;
|
||||
norm2[i] = hist2[i] / sum2;
|
||||
}
|
||||
|
||||
// 计算加权EMD
|
||||
double emd = 0.0;
|
||||
double flow = 0.0;
|
||||
for (int i = 0; i < norm1.length; i++) {
|
||||
flow += norm1[i] - norm2[i];
|
||||
emd += Math.abs(flow);
|
||||
}
|
||||
|
||||
return Math.min(1.0, emd / norm1.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算图像的亮度直方图
|
||||
*
|
||||
* @param image 图像
|
||||
* @return 亮度直方图
|
||||
*/
|
||||
private static int[] calculateBrightnessHistogram(BufferedImage image) {
|
||||
int[] histogram = new int[256];
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int gray = getGrayValue(image, x, y);
|
||||
histogram[gray]++;
|
||||
}
|
||||
}
|
||||
|
||||
return histogram;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算直方图的累积分布函数(CDF)
|
||||
*
|
||||
* @param histogram 直方图
|
||||
* @return 累积分布函数
|
||||
*/
|
||||
private static double[] calculateCDF(int[] histogram) {
|
||||
double[] cdf = new double[histogram.length];
|
||||
int sum = 0;
|
||||
|
||||
// 计算总像素数
|
||||
for (int value : histogram) {
|
||||
sum += value;
|
||||
}
|
||||
|
||||
// 避免除以零
|
||||
if (sum == 0) {
|
||||
return cdf;
|
||||
}
|
||||
|
||||
// 计算CDF
|
||||
int runningSum = 0;
|
||||
for (int i = 0; i < histogram.length; i++) {
|
||||
runningSum += histogram[i];
|
||||
cdf[i] = (double) runningSum / sum;
|
||||
}
|
||||
|
||||
return cdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取人脸尺寸信息
|
||||
*
|
||||
* @param imageUrl 图片URL
|
||||
* @return 人脸尺寸信息字符串,如果未检测到人脸则返回"未检测到人脸"
|
||||
*/
|
||||
private static String getFaceInfo(String imageUrl) {
|
||||
try {
|
||||
BufferedImage image = downloadImage(imageUrl);
|
||||
if (image == null) {
|
||||
return "无法下载图片";
|
||||
}
|
||||
|
||||
Rect face = detectFace(image);
|
||||
if (face == null) {
|
||||
return "未检测到人脸";
|
||||
}
|
||||
|
||||
boolean isSmallFace = (face.width < SMALL_FACE_THRESHOLD || face.height < SMALL_FACE_THRESHOLD);
|
||||
return String.format("人脸尺寸: %dx%d, %s", face.width, face.height,
|
||||
isSmallFace ? "小尺寸人脸" : "正常尺寸人脸");
|
||||
} catch (Exception e) {
|
||||
return "获取人脸信息失败: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("开始测试人脸相似度算法...");
|
||||
|
||||
|
@ -401,6 +953,14 @@ public class ImageSimilarityUtils {
|
|||
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";
|
||||
|
||||
// 打印每张图片的人脸信息
|
||||
System.out.println("\n图片人脸信息:");
|
||||
System.out.println("图片1: " + getFaceInfo(userPicture1));
|
||||
System.out.println("图片2: " + getFaceInfo(userPicture2));
|
||||
System.out.println("图片3: " + getFaceInfo(userPicture3));
|
||||
System.out.println("图片4: " + getFaceInfo(userPicture4));
|
||||
System.out.println("图片5: " + getFaceInfo(userPicture5));
|
||||
System.out.println();
|
||||
|
||||
Map<String,String[]> map = new HashMap<>();
|
||||
map.put("1-2",new String[]{userPicture1,userPicture2});
|
||||
|
@ -414,6 +974,7 @@ public class ImageSimilarityUtils {
|
|||
map.put("3-5",new String[]{userPicture3,userPicture5});
|
||||
map.put("4-5",new String[]{userPicture4,userPicture5});
|
||||
|
||||
System.out.println("相似度测试结果:");
|
||||
for (String key : map.keySet()) {
|
||||
String[] strings = map.get(key);
|
||||
String img1 = strings[0];
|
||||
|
|
|
@ -408,7 +408,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
a.workParts,
|
||||
a.changeInfo,
|
||||
a.lordSent,
|
||||
CONCAT(a.lordSentUser ,'【',b.sub_dept_name,'】',b.user_phone) as lordSentUser,
|
||||
CONCAT(ifnull(a.lordSentUser,a.lordSent) ,'【',ifnull(b.sub_dept_name,'施工单位'),'】',ifnull(b.user_phone,'')) as lordSentUser,
|
||||
a.lordSentUser,
|
||||
a.copySend,
|
||||
a.copySendUser,
|
||||
|
@ -423,14 +423,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
a.checkUserPhone,
|
||||
a.smark_url,
|
||||
a.isDel,
|
||||
CONCAT(c.nick_name,'【',c.phonenumber,'】') as createUser,
|
||||
CONCAT(ifnull(c.nick_name,''),'【',ifnull(c.phonenumber,''),'】') as createUser,
|
||||
a.createUser as updateUser,
|
||||
a.createTime,
|
||||
a.updateUser,
|
||||
a.updateTime,
|
||||
a.danger_type,
|
||||
a.recheckSend,
|
||||
CONCAT(a.recheckSendUser ,'【',d.sub_dept_name,'】',d.user_phone) as recheckSendUser,
|
||||
CONCAT(ifnull(a.recheckSendUser,a.recheckSend) ,'【',ifnull(d.sub_dept_name,'施工单位'),'】',ifnull(d.user_phone,'')) as recheckSendUser,
|
||||
a.recheckSendUser,
|
||||
a.roleType,
|
||||
a.problemType
|
||||
|
|
|
@ -4,6 +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.poi.ExcelUtil;
|
||||
import com.yanzhu.common.core.web.controller.BaseController;
|
||||
import com.yanzhu.common.core.web.domain.AjaxResult;
|
||||
|
@ -126,9 +127,9 @@ public class ProMobileAttendanceConfigController extends BaseController
|
|||
String attImg=attData.getAttImg();
|
||||
|
||||
// 使用专门为人脸识别考勤优化的相似度计算
|
||||
boolean isMatch = com.yanzhu.common.core.utils.ImageSimilarityUtils.isFaceMatchForAttendance(userPicture, attImg);
|
||||
double similarity = ImageSimilarityUtils.calculateFaceSimilarity(userPicture, attImg);
|
||||
|
||||
if (isMatch) {
|
||||
if (similarity>=0.8) {
|
||||
// 相似度达标,增加考勤数据
|
||||
// TODO: 增加考勤数据逻辑
|
||||
// TODO: 增加考勤历史记录逻辑
|
||||
|
|
|
@ -48,6 +48,8 @@ public interface IProProjectInfoSubdeptsUsersService
|
|||
*/
|
||||
public List<ProProjectInfoSubdeptsUsers> findAllProSubDeptsUser(ProProjectInfoSubdeptsUsers proProjectInfoSubdeptsUsers);
|
||||
|
||||
public int addProProjectInfoSubdeptsUsers(ProProjectInfoSubdeptsUsers proProjectInfoSubdeptsUsers);
|
||||
|
||||
/**
|
||||
* 新增分包单位工人
|
||||
*
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.yanzhu.manage.mapper.ProProjectInfoSubdeptsGroupMapper;
|
|||
import com.yanzhu.manage.mapper.ProProjectInfoSubdeptsMapper;
|
||||
import com.yanzhu.manage.mapper.ProProjectInfoSubdeptsUsersMapper;
|
||||
import com.yanzhu.manage.service.IProProjectInfoSubdeptsGroupService;
|
||||
import com.yanzhu.manage.service.IProProjectInfoSubdeptsUsersService;
|
||||
import com.yanzhu.manage.service.IUniService;
|
||||
import com.yanzhu.manage.utils.pdf.FileUtil;
|
||||
import com.yanzhu.system.api.RemoteUserService;
|
||||
|
@ -60,6 +61,9 @@ public class ProProjectInfoSubdeptsGroupServiceImpl implements IProProjectInfoSu
|
|||
@Autowired
|
||||
private ProProjectInfoSubdeptsUsersMapper proProjectInfoSubdeptsUsersMapper;
|
||||
|
||||
@Autowired
|
||||
private IProProjectInfoSubdeptsUsersService proProjectInfoSubdeptsUsersService;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ProProjectInfoSubdeptsGroupServiceImpl.class);
|
||||
|
||||
/**
|
||||
|
@ -178,7 +182,7 @@ public class ProProjectInfoSubdeptsGroupServiceImpl implements IProProjectInfoSu
|
|||
subdeptsUser.setCreateBy(SecurityUtils.getUsername());
|
||||
subdeptsUser.setApproveStatus(ApproveStatus.passed.getCode());
|
||||
subdeptsUser.setCreateTime(DateUtils.getNowDate());
|
||||
res = proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
res = proProjectInfoSubdeptsUsersService.addProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
// 系统添加的人员默认增加入场信息
|
||||
uniService.syncUniUser(subdeptsUser,true);
|
||||
if(res>0){
|
||||
|
@ -285,7 +289,7 @@ public class ProProjectInfoSubdeptsGroupServiceImpl implements IProProjectInfoSu
|
|||
subdeptsUser.setCreateBy(SecurityUtils.getUsername());
|
||||
subdeptsUser.setApproveStatus(ApproveStatus.passed.getCode());
|
||||
subdeptsUser.setCreateTime(DateUtils.getNowDate());
|
||||
res = proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
res = proProjectInfoSubdeptsUsersService.addProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
if(res>0){
|
||||
try {
|
||||
String accessToken = wxMaService.getAccessToken();
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.yanzhu.manage.mapper.ProProjectInfoSubdeptsGroupMapper;
|
|||
import com.yanzhu.manage.mapper.ProProjectInfoSubdeptsMapper;
|
||||
import com.yanzhu.manage.mapper.ProProjectInfoSubdeptsUsersMapper;
|
||||
import com.yanzhu.manage.service.IProProjectInfoSubdeptsService;
|
||||
import com.yanzhu.manage.service.IProProjectInfoSubdeptsUsersService;
|
||||
import com.yanzhu.manage.service.IUniService;
|
||||
import com.yanzhu.manage.utils.pdf.FileUtil;
|
||||
import com.yanzhu.system.api.RemoteUserService;
|
||||
|
@ -69,6 +70,9 @@ public class ProProjectInfoSubdeptsServiceImpl implements IProProjectInfoSubdept
|
|||
@Autowired
|
||||
private ProProjectInfoSubdeptsGroupMapper proProjectInfoSubdeptsGroupMapper;
|
||||
|
||||
@Autowired
|
||||
private IProProjectInfoSubdeptsUsersService proProjectInfoSubdeptsUsersService;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ProProjectInfoSubdeptsServiceImpl.class);
|
||||
|
||||
/**
|
||||
|
@ -211,7 +215,7 @@ public class ProProjectInfoSubdeptsServiceImpl implements IProProjectInfoSubdept
|
|||
subdeptsUser.setCreateBy(SecurityUtils.getUsername());
|
||||
subdeptsUser.setApproveStatus(ApproveStatus.passed.getCode());
|
||||
subdeptsUser.setCreateTime(DateUtils.getNowDate());
|
||||
int r = proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
int r = proProjectInfoSubdeptsUsersService.addProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
// 系统添加的人员默认增加入场信息
|
||||
uniService.syncUniUser(subdeptsUser,true);
|
||||
try {
|
||||
|
@ -353,7 +357,7 @@ public class ProProjectInfoSubdeptsServiceImpl implements IProProjectInfoSubdept
|
|||
}
|
||||
subdeptsUser.setCreateBy(DataSourceEnuns.APP.getInfo());
|
||||
subdeptsUser.setCreateTime(DateUtils.getNowDate());
|
||||
res = proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
res = proProjectInfoSubdeptsUsersService.addProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
if(isSign){
|
||||
try {
|
||||
if(res>0){
|
||||
|
@ -446,7 +450,7 @@ public class ProProjectInfoSubdeptsServiceImpl implements IProProjectInfoSubdept
|
|||
if(Objects.isNull(subdeptsUser.getId())){
|
||||
subdeptsUser.setCreateBy(SecurityUtils.getUsername());
|
||||
subdeptsUser.setCreateTime(DateUtils.getNowDate());
|
||||
proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
proProjectInfoSubdeptsUsersService.addProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
}else{
|
||||
subdeptsUser.setUpdateBy(SecurityUtils.getUsername());
|
||||
subdeptsUser.setUpdateTime(DateUtils.getNowDate());
|
||||
|
@ -519,7 +523,7 @@ public class ProProjectInfoSubdeptsServiceImpl implements IProProjectInfoSubdept
|
|||
if(Objects.isNull(subdeptsUser.getId())){
|
||||
subdeptsUser.setCreateBy(DataSourceEnuns.APP.getInfo());
|
||||
subdeptsUser.setCreateTime(DateUtils.getNowDate());
|
||||
proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
proProjectInfoSubdeptsUsersService.addProProjectInfoSubdeptsUsers(subdeptsUser);
|
||||
}else{
|
||||
subdeptsUser.setUpdateBy(DataSourceEnuns.APP.getInfo());
|
||||
subdeptsUser.setUpdateTime(DateUtils.getNowDate());
|
||||
|
|
|
@ -83,6 +83,10 @@ public class ProProjectInfoSubdeptsUsersServiceImpl implements IProProjectInfoSu
|
|||
@Autowired
|
||||
private SysUserMapper sysUserMapper;
|
||||
|
||||
@Autowired
|
||||
private ProProjectInfoMapper proProjectInfoMapper;
|
||||
|
||||
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ProProjectInfoSubdeptsUsersServiceImpl.class);
|
||||
|
||||
|
@ -140,6 +144,16 @@ public class ProProjectInfoSubdeptsUsersServiceImpl implements IProProjectInfoSu
|
|||
return proProjectInfoSubdeptsUsersMapper.selectProProjectInfoSubdeptsUsersList(proProjectInfoSubdeptsUsers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addProProjectInfoSubdeptsUsers(ProProjectInfoSubdeptsUsers proProjectInfoSubdeptsUsers) {
|
||||
if (proProjectInfoSubdeptsUsers.getSubDeptId() == 1L) {
|
||||
ProProjectInfo proProjectInfo = proProjectInfoMapper.selectProProjectInfoById(proProjectInfoSubdeptsUsers.getProjectId());
|
||||
if (proProjectInfo != null) {
|
||||
proProjectInfoSubdeptsUsers.setSubDeptName(proProjectInfo.getProjectName());
|
||||
}
|
||||
}
|
||||
return proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(proProjectInfoSubdeptsUsers);
|
||||
}
|
||||
/**
|
||||
* 新增分包单位工人
|
||||
*
|
||||
|
@ -238,7 +252,7 @@ public class ProProjectInfoSubdeptsUsersServiceImpl implements IProProjectInfoSu
|
|||
R<Long> tmp=remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER);
|
||||
Long userId= tmp.getData();
|
||||
proProjectInfoSubdeptsUsers.setUserId(userId);
|
||||
int res = proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(proProjectInfoSubdeptsUsers);
|
||||
int res = addProProjectInfoSubdeptsUsers(proProjectInfoSubdeptsUsers);
|
||||
uniService.syncUniUser(proProjectInfoSubdeptsUsers,true);
|
||||
String userPost = proProjectInfoSubdeptsUsers.getUserPost();
|
||||
if(res>0 && (Objects.equals(UserPostEnums.WTDL.getCode(),userPost) || Objects.equals(UserPostEnums.XMJL.getCode(),userPost) || Objects.equals(UserPostEnums.BZZ.getCode(),userPost))){
|
||||
|
@ -319,7 +333,7 @@ public class ProProjectInfoSubdeptsUsersServiceImpl implements IProProjectInfoSu
|
|||
}
|
||||
proProjectInfoSubdeptsUsers.setSubDeptGroup(proProjectInfoSubdeptsGroup.getId());
|
||||
proProjectInfoSubdeptsUsers.setSubDeptGroupName(proProjectInfoSubdeptsGroup.getGroupName());
|
||||
proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(proProjectInfoSubdeptsUsers);
|
||||
addProProjectInfoSubdeptsUsers(proProjectInfoSubdeptsUsers);
|
||||
uniService.syncUniUser(proProjectInfoSubdeptsUsers,true);
|
||||
}else {
|
||||
ProProjectInfoSubdeptsUsers deptUser = users.get(0);
|
||||
|
@ -536,7 +550,7 @@ public class ProProjectInfoSubdeptsUsersServiceImpl implements IProProjectInfoSu
|
|||
sysUser.setRemark(proProjectInfoSubdeptsUsers.getSubDeptName());
|
||||
Long userId = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER).getData();
|
||||
proProjectInfoSubdeptsUsers.setUserId(userId);
|
||||
proProjectInfoSubdeptsUsersMapper.insertProProjectInfoSubdeptsUsers(proProjectInfoSubdeptsUsers);
|
||||
addProProjectInfoSubdeptsUsers(proProjectInfoSubdeptsUsers);
|
||||
|
||||
if(isSign){
|
||||
if(Objects.equals(proProjectInfoSubdeptsUsers.getUserPost(),UserPostEnums.XMJL.getCode())){
|
||||
|
|
|
@ -2,6 +2,9 @@ import request from "@/utils/request";
|
|||
|
||||
// 查询安全隐患整改列表
|
||||
export function listProblemmodify(query) {
|
||||
if(!query.isNew){
|
||||
query.isNew = 1;
|
||||
}
|
||||
return request({
|
||||
url: "/manage/problemmodify/list",
|
||||
method: "get",
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<el-button @click="GetViewPort">添加轨迹点</el-button>
|
||||
</el-form-item>
|
||||
<div>
|
||||
<el-table :data="form.viewPortPoints" border style="width: 100%">
|
||||
<el-table :data="form.viewPortPoints" border style="width: 100%" max-height="50vh">
|
||||
<el-table-column prop="name" label="视点">
|
||||
<template #default="scope">{{ '视点' + (++scope.$index) }}</template>
|
||||
</el-table-column>
|
||||
|
|
Loading…
Reference in New Issue