车车 7 месяцев назад
Родитель
Сommit
bbaefc83a0

+ 111 - 0
ktg-common/src/main/java/com/ktg/common/utils/face/FaceImgMatchUtil.java

@@ -0,0 +1,111 @@
+package com.ktg.common.utils.face;
+
+
+import com.ktg.common.utils.StringUtils;
+import com.ktg.common.vo.FaceMatchVO;
+import org.opencv.core.*;
+import org.opencv.imgcodecs.Imgcodecs;
+import org.opencv.imgproc.Imgproc;
+import org.opencv.objdetect.CascadeClassifier;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.util.Arrays;
+
+/**
+ * 面部对比工具
+ *
+ * @author ruoyi
+ */
+public class FaceImgMatchUtil {
+
+    @Value("${ktg-mes.prod}")
+    private static String prodApi;
+
+    // 加载OpenCV本地库
+    static {
+        String loadPath = "/guoruan/app/opencv/build/java/x64/opencv_java470.dll";
+        if (StringUtils.isBlank(prodApi)) {
+            loadPath = "C:/work/app/install/opencv/build/java/x64/opencv_java470.dll";
+        }
+        System.load(loadPath);
+    }
+
+    // 初始化人脸探测器
+    private static CascadeClassifier faceDetector;
+
+    static {
+        String faceDetectorPath = "/guoruan/app/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
+        if (StringUtils.isBlank(prodApi)) {
+            faceDetectorPath = "C:/work/app/install/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
+        }
+        faceDetector = new CascadeClassifier(faceDetectorPath);
+    }
+
+    public static void main(String[] args) {
+        FaceMatchVO vo = faceRecognitionComparison("C:/work/file/11.jpg", "C:/work/file/13.jpg");
+        System.out.println("对比结果:" + vo.getScore());
+        if (vo.getScore() > 0.85) {
+            System.out.println("人脸匹配成功1");
+        } else {
+            System.out.println("人脸不匹配识别1");
+        }
+
+        FaceMatchVO vo1 = faceRecognitionComparison("C:/work/file/12.jpg", "C:/work/file/13.jpg");
+        System.out.println("对比结果:" + vo1.getScore());
+        if (vo1.getScore() > 0.85) {
+            System.out.println("人脸匹配成功2");
+        } else {
+            System.out.println("人脸不匹配识别2");
+        }
+        // 终止当前运行的 Java 虚拟机。
+        System.exit(0);
+    }
+
+
+    /**
+     * 人脸识别比对
+     */
+    public static FaceMatchVO faceRecognitionComparison(String image1, String image2) {
+        Mat mat1 = conv_mat(image1);
+        Mat mat2 = conv_mat(image2);
+        Mat mat3 = new Mat();
+        Mat mat4 = new Mat();
+        // 颜色范围
+        MatOfFloat ranges = new MatOfFloat(0f, 256f);
+        // 直方图大小, 越大匹配越精确 (越慢)
+        MatOfInt histSize = new MatOfInt(1000);
+
+        Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
+
+        // 比较两个密集或两个稀疏直方图
+
+        Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);
+        double v = Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
+        if (v > 0.80) {
+            return new FaceMatchVO().setContent(image2).setScore(v);
+        }
+        return null;
+    }
+
+    /**
+     * 灰度化人脸
+     */
+    public static Mat conv_mat(String img) {
+        // 读取图像
+        Mat mat1 = Imgcodecs.imread(img);
+        Mat mat2 = new Mat();
+        // 灰度化:将图像从一种颜色空间转换为另一种颜色空间
+        Imgproc.cvtColor(mat1, mat2, Imgproc.COLOR_BGR2GRAY);
+        // 探测人脸:检测到的对象作为矩形列表返回
+        MatOfRect faceDetections = new MatOfRect();
+        faceDetector.detectMultiScale(mat1, faceDetections);
+        // rect中人脸图片的范围
+        for (Rect rect : faceDetections.toArray()) {
+            Mat face = new Mat(mat1, rect);
+            return face;
+        }
+        return null;
+    }
+
+
+}

+ 85 - 63
ktg-common/src/main/java/com/ktg/common/utils/face/FaceMatchUtil.java

@@ -8,9 +8,14 @@ import org.opencv.imgcodecs.Imgcodecs;
 import org.opencv.imgproc.Imgproc;
 import org.opencv.objdetect.CascadeClassifier;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.multipart.MultipartFile;
 
-import java.util.Arrays;
-import java.util.Set;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 /**
  * 面部对比工具
@@ -19,21 +24,23 @@ import java.util.Set;
  */
 public class FaceMatchUtil
 {
-    // 初始化人脸探测器
-    static CascadeClassifier faceDetector;
 
     @Value("${ktg-mes.prod}")
     private static String prodApi;
 
+    // 加载OpenCV本地库
     static {
-        // 加载OpenCV本地库
-        // System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
         String loadPath = "/guoruan/app/opencv/build/java/x64/opencv_java470.dll";
         if (StringUtils.isBlank(prodApi)) {
             loadPath = "C:/work/app/install/opencv/build/java/x64/opencv_java470.dll";
         }
         System.load(loadPath);
-        // 从配置文件lbpcascade_frontalface.xml中创建一个人脸识别器,文件位于opencv安装目录中
+    }
+
+    // 初始化人脸探测器
+    private static CascadeClassifier faceDetector;
+
+    static {
         String faceDetectorPath = "/guoruan/app/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
         if (StringUtils.isBlank(prodApi)) {
             faceDetectorPath = "C:/work/app/install/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
@@ -41,73 +48,88 @@ public class FaceMatchUtil
         faceDetector = new CascadeClassifier(faceDetectorPath);
     }
 
-   /* public static void main(String[] args) {
-        double comparison = faceRecognitionComparison("C:/work/file/11.jpg", "C:/work/file/13.jpg");
-        System.out.println("对比结果:" + comparison);
-        if (comparison > 0.85) {
-            System.out.println("人脸匹配成功1");
-        } else {
-            System.out.println("人脸不匹配识别1");
-        }
-
-        double comparison2 = faceRecognitionComparison("C:/work/file/12.jpg", "C:/work/file/13.jpg");
-        System.out.println("对比结果:" + comparison2);
-        if (comparison2 > 0.85) {
-            System.out.println("人脸匹配成功2");
-        } else {
-            System.out.println("人脸不匹配识别2");
-        }
-        // 终止当前运行的 Java 虚拟机。
-        System.exit(0);
-    }*/
-
-
     /**
-     * 人脸识别
+     * 人脸识别对比
      */
-    public static FaceMatchVO faceRecognitionComparison(String image1, Set<String> image2) {
-        Mat mat1 = conv_Mat(image1);
+    public static FaceMatchVO faceRecognitionComparison(MultipartFile mfile, Set<String> image2) throws IOException {
+        Mat mat1 = conv_mat(mfile); // 转换为灰度图像的Mat对象
+        Mat hist1 = computeHistogram(mat1); // 计算第一个图像的直方图
 
-        Mat mat3 = new Mat();
-        Mat mat4 = new Mat();
-        // 颜色范围
-        MatOfFloat ranges = new MatOfFloat(0f, 256f);
-        // 直方图大小, 越大匹配越精确 (越慢)
-        MatOfInt histSize = new MatOfInt(1000);
+        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); // 创建线程池
+        List<Future<FaceMatchVO>> futures = new ArrayList<>(); // 存储Future对象的列表
 
-        Imgproc.calcHist(Arrays.asList(mat1), new MatOfInt(0), new Mat(), mat3, histSize, ranges);
+        for (String imgPath : image2) { // 遍历第二个图像集合
+            futures.add(executorService.submit(() -> {
+                Mat mat2 = conv_Mat(imgPath); // 将路径对应的图像转换为灰度图像的Mat对象
+                if (mat2 == null) {
+                    return null; // 如果转换失败,返回null
+                }
+                Mat hist2 = computeHistogram(mat2); // 计算第二个图像的直方图
+                double v = Imgproc.compareHist(hist1, hist2, Imgproc.CV_COMP_CORREL); // 比较两个直方图的相似度
+                if (v > 0.80) { // 如果相似度大于0.80,认为匹配成功
+                    return new FaceMatchVO().setContent(imgPath).setScore(v); // 返回匹配结果
+                } else {
+                    return null; // 否则返回null
+                }
+            }));
+        }
 
-        // 比较两个密集或两个稀疏直方图
-        for (String s : image2) {
-            Mat mat2 = conv_Mat(s);
-            Imgproc.calcHist(Arrays.asList(mat2), new MatOfInt(0), new Mat(), mat4, histSize, ranges);
-            double v = Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
-            System.out.println("匹配人脸:" + s + ",分数:" + v);
-            if (v > 0.80) {
-                System.out.println("匹配到了合适的人脸信息,人脸照片" + s + ",人脸分数" + v);
-                return new FaceMatchVO().setContent(s).setScore(v);
+        try {
+            for (Future<FaceMatchVO> future : futures) { // 遍历所有Future对象
+                FaceMatchVO result = future.get(); // 获取执行结果
+                if (result != null) { // 如果结果不为空,说明找到了匹配的图像
+                    executorService.shutdownNow(); // 立即关闭线程池
+                    return result; // 返回匹配结果
+                }
             }
+        } catch (InterruptedException | ExecutionException e) { // 捕获异常
+            e.printStackTrace(); // 打印异常信息
+        } finally {
+            executorService.shutdown(); // 确保线程池最终被关闭
+        }
+        return null; // 如果没有找到匹配的图像,返回null
+    }
+
+    /**
+     * 灰度化人脸并返回Mat对象
+     */
+    public static Mat conv_mat(MultipartFile mfile) throws IOException {
+        byte[] bytes = mfile.getBytes(); // 获取文件字节数组
+        Mat mat1 = Imgcodecs.imdecode(new MatOfByte(bytes), Imgcodecs.IMREAD_COLOR); // 解码为彩色图像的Mat对象
+        Mat mat2 = new Mat(); // 创建一个新的Mat对象用于存储灰度图像
+        Imgproc.cvtColor(mat1, mat2, Imgcodecs.IMREAD_COLOR); // 将彩色图像转换为灰度图像
+        MatOfRect faceDetections = new MatOfRect(); // 创建用于存储检测到的人脸区域的MatOfRect对象
+        faceDetector.detectMultiScale(mat1, faceDetections); // 检测人脸区域
+        for (Rect rect : faceDetections.toArray()) { // 遍历检测到的人脸区域
+            return new Mat(mat1, rect); // 返回包含人脸区域的子Mat对象
         }
-        return null;
+        return null; // 如果没有检测到人脸,返回null
     }
 
     /**
-     * 灰度化人脸
+     * 灰度化人脸并返回Mat对象
      */
-    public static Mat conv_Mat(String img) {
-        // 读取图像
-        Mat mat1 = Imgcodecs.imread(img);
-        Mat mat2 = new Mat();
-        // 灰度化:将图像从一种颜色空间转换为另一种颜色空间
-        Imgproc.cvtColor(mat1, mat2, Imgproc.COLOR_BGR2GRAY);
-        // 探测人脸:检测到的对象作为矩形列表返回
-        MatOfRect faceDetections = new MatOfRect();
-        faceDetector.detectMultiScale(mat1, faceDetections);
-        // rect中人脸图片的范围
-        for (Rect rect : faceDetections.toArray()) {
-            Mat face = new Mat(mat1, rect);
-            return face;
+    public static Mat conv_Mat(String imgPath) {
+        Mat mat1 = Imgcodecs.imread(imgPath); // 读取图像文件并转换为Mat对象
+        Mat mat2 = new Mat(); // 创建一个新的Mat对象用于存储灰度图像
+        Imgproc.cvtColor(mat1, mat2, Imgcodecs.IMREAD_COLOR); // 将彩色图像转换为灰度图像
+        MatOfRect faceDetections = new MatOfRect(); // 创建用于存储检测到的人脸区域的MatOfRect对象
+        faceDetector.detectMultiScale(mat1, faceDetections); // 检测人脸区域
+        for (Rect rect : faceDetections.toArray()) { // 遍历检测到的人脸区域
+            return new Mat(mat1, rect); // 返回包含人脸区域的子Mat对象
         }
-        return null;
+        return null; // 如果没有检测到人脸,返回null
+    }
+
+    /**
+     * 计算直方图
+     */
+    private static Mat computeHistogram(Mat mat) {
+        Mat hist = new Mat(); // 创建用于存储直方图的Mat对象
+        MatOfFloat ranges = new MatOfFloat(0f, 256f); // 定义直方图的范围
+        MatOfInt histSize = new MatOfInt(1000); // 定义直方图的大小(越大越精确,但计算速度越慢)
+        Imgproc.calcHist(Collections.singletonList(mat), new MatOfInt(0), new Mat(), hist, histSize, ranges); // 计算直方图
+        Core.normalize(hist, hist, 0, 1, Core.NORM_MINMAX); // 归一化直方图,使其值在0到1之间
+        return hist; // 返回计算好的直方图
     }
 }

+ 30 - 31
ktg-framework/src/main/java/com/ktg/framework/web/service/SysLoginService.java

@@ -260,41 +260,40 @@ public class SysLoginService {
         Assert.isFalse(list.isEmpty(), "人脸库中暂无您的人脸信息!");
         List<String> collect = list.stream().map(SysUserCharacteristic::getContent).collect(Collectors.toList());
         // 通过人脸获取最相似的用户
+        FaceCutVO faceCutVO = null;
+        FaceMatchVO faceMatchVO;
+        try {
+            // 记录开始时间
+            long startTime = System.currentTimeMillis();
+            faceCutVO = FaceCutUtil.imageFaceDetection(file, 0L, null);
 
-
-        // 记录开始时间
-        long startTime = System.currentTimeMillis();
-        FaceCutVO faceCutVO = FaceCutUtil.imageFaceDetection(file, 0L, null);
-
-        // 记录结束时间
-        long endTime = System.currentTimeMillis();
-        // 计算时间差(毫秒)
-        long duration = endTime - startTime;
-        System.out.println("Execution time in milliseconds: " + duration);
-
-
-        // 记录开始时间
-        long startTime1 = System.currentTimeMillis();
-        FaceMatchVO faceMatchVO = FaceMatchUtil.faceRecognitionComparison(faceCutVO.getContent(), new HashSet<>(collect));
-
-        // 记录结束时间
-        long endTime1 = System.currentTimeMillis();
-        // 计算时间差(毫秒)
-        long duration1 = endTime1 - startTime1;
-        System.out.println("Execution time in milliseconds: " + duration);
-
+            // 记录结束时间
+            long endTime = System.currentTimeMillis();
+            // 计算时间差(毫秒)
+            long duration = endTime - startTime;
+            System.out.println("Execution time in milliseconds: " + duration);
 
 
-        // 删除刚刚上传的文件信息
-        File file1 = new File(faceCutVO.getContent());
-        if (!file1.delete()) {
-            log.error("面部临时裁剪文件删除失败: " + faceCutVO.getContent());
-        }
-        File file2 = new File(faceCutVO.getImagePath());
-        if (!file2.delete()) {
-            log.error("img文件删除失败: " + faceCutVO.getImagePath());
+            // 记录开始时间
+            long startTime1 = System.currentTimeMillis();
+            faceMatchVO = FaceMatchUtil.faceRecognitionComparison(file, new HashSet<>(collect));
+            // 记录结束时间
+            long endTime1 = System.currentTimeMillis();
+            // 计算时间差(毫秒)
+            long duration1 = endTime1 - startTime1;
+            System.out.println("Execution time in milliseconds: " + duration1);
+            Assert.notNull(faceMatchVO, "无法根据人脸确定您的身份,请通过其它方式登录!");
+        } finally {
+            // 删除刚刚上传的文件信息
+            File file1 = new File(faceCutVO.getContent());
+            if (!file1.delete()) {
+                log.error("面部临时裁剪文件删除失败: " + faceCutVO.getContent());
+            }
+            File file2 = new File(faceCutVO.getImagePath());
+            if (!file2.delete()) {
+                log.error("img文件删除失败: " + faceCutVO.getImagePath());
+            }
         }
-        Assert.notNull(faceMatchVO, "无法根据人脸确定您的身份,请通过其它方式登录!");
         String okFace = faceMatchVO.getContent();
         SysUserCharacteristic one = iSysUserCharacteristicService.getOne(Wrappers.<SysUserCharacteristic>lambdaQuery()
                 .eq(SysUserCharacteristic::getContent, okFace)