车车 7 maanden geleden
bovenliggende
commit
953d3cbdd8

+ 2 - 2
ktg-admin/src/main/java/com/ktg/web/controller/system/SysLoginController.java

@@ -117,8 +117,8 @@ public class SysLoginController {
     @ApiOperation("系统用户登录-指纹验证登录dat")
     @PostMapping("/loginByFingerprintDat")
     public AjaxResult loginByFingerprintDat(MultipartFile file) throws IOException {
-        // AjaxResult ajaxResult = loginService.loginByFingerprintDat(file);
-        AjaxResult ajaxResult = loginService.loginByFace(file);
+        AjaxResult ajaxResult = loginService.loginByFingerprintDat(file);
+        // AjaxResult ajaxResult = loginService.loginByFace(file);
         return ajaxResult;
     }
 

+ 3 - 3
ktg-admin/src/main/java/com/ktg/web/controller/system/SysUserCharacteristicController.java

@@ -89,7 +89,7 @@ public class SysUserCharacteristicController extends BaseController
     // @PreAuthorize("@ss.hasPermi('iscs:characteristic:add')")
     @Log(title = "新增指纹录入-指纹图片转成dat存储", businessType = BusinessType.INSERT)
     @PostMapping("/insertUserFingerprintDat")
-    public CommonResult<FaceCutVO> insertUserFingerprintDat(MultipartFile file, String userName) throws IOException {
+    public CommonResult<Boolean> insertUserFingerprintDat(MultipartFile file, String userName) throws IOException {
         IsSystemAttribute one = isSystemAttributeService.getOne(Wrappers.<IsSystemAttribute>lambdaQuery()
                 .eq(IsSystemAttribute::getSysAttrKey, "sys.fingerprint.limit"));
         String sysAttrValue = null;
@@ -97,8 +97,8 @@ public class SysUserCharacteristicController extends BaseController
             sysAttrValue = one.getSysAttrValue();
         }
         String url = serverConfig.getUrl();
-        // return CommonResult.success(sysUserCharacteristicService.insertUserFingerprintDat(file, userName, sysAttrValue, url));
-        return CommonResult.success(sysUserCharacteristicService.insertUserCutFace(file, userName, sysAttrValue, url));
+        return CommonResult.success(sysUserCharacteristicService.insertUserFingerprintDat(file, userName, sysAttrValue, url));
+        // return CommonResult.success(sysUserCharacteristicService.insertUserCutFace(file, userName, sysAttrValue, url));
     }
     @ApiOperation("修改用户特征(指纹、面部)")
     @PreAuthorize("@ss.hasPermi('iscs:characteristic:edit')")

+ 3 - 3
ktg-common/src/main/java/com/ktg/common/utils/face/FaceCutUtil.java

@@ -46,7 +46,7 @@ public class FaceCutUtil
         if (StringUtils.isBlank(prodApi)) {
             faceDetectorPath = "C:/work/app/install/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
         }
-        CascadeClassifier faceDetector = new CascadeClassifier(faceDetectorPath);
+        faceDetector = new CascadeClassifier(faceDetectorPath);
 
         // -------------------- 定义文件的基础属性----------------------------
         // 人脸存储基础路径
@@ -141,8 +141,8 @@ public class FaceCutUtil
         Mat image = Imgcodecs.imread(readPath);
         // 截取的区域
         Rect rect = new Rect(x, y, width, height);
-        // Mat sub = new Mat(image,rect);
-        Mat sub = image.submat(rect);
+        Mat sub = new Mat(image,rect);
+        // Mat sub = image.submat(rect);
         Mat mat = new Mat();
         Size size = new Size(width, height);
         // 人脸进行截图并保存

+ 193 - 0
ktg-common/src/main/java/com/ktg/common/utils/face/FaceCutUtil1.java

@@ -0,0 +1,193 @@
+package com.ktg.common.utils.face;
+
+import cn.hutool.core.lang.Assert;
+import com.ktg.common.config.RuoYiConfig;
+import com.ktg.common.utils.StringUtils;
+import com.ktg.common.vo.FaceCutVO;
+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 org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+
+/**
+ * 面部裁剪工具
+ *
+ * @author cgj
+ */
+public class FaceCutUtil1
+{
+
+    @Value("${ktg-mes.prod}")
+    private static String prodApi;
+
+
+    // 初始化人脸探测器
+    static CascadeClassifier faceDetector;
+    /**
+     * 图片人脸检测
+     */
+    public static FaceCutVO imageFaceDetection(MultipartFile file, Long userId, String url) throws IOException {
+        // ------------------------加载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安装目录中
+        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);
+
+        // -------------------- 定义文件的基础属性----------------------------
+        // 人脸存储基础路径
+        String profile = RuoYiConfig.getProfile();
+        String basePath = profile + "/face/" + userId +"/";
+        if (StringUtils.isBlank(prodApi)) {
+            basePath = "C:" + basePath;
+        }
+        // 原文件裁剪后的路径
+        String content = null;
+        // 原文件转存后的路径
+        String imagePath;
+        // 原文件转存后的前端请求路径
+        String imageUrl = null;
+
+        // 时间戳
+        long currentTimestamp = System.currentTimeMillis();
+        // 设置文件路径
+        String filePath = basePath ;
+        // 确保目录存在,不存在则创建一个
+        Path uploadPath = Paths.get(filePath);
+        if (!Files.exists(uploadPath)) {
+            Files.createDirectories(uploadPath);
+        }
+        // 获取文件名并构建目标路径
+        String fileName = userId + "_" + currentTimestamp + "_" + file.getOriginalFilename();
+        Path targetLocation = uploadPath.resolve(fileName);
+        imagePath = filePath + fileName;
+        // 获取前端的请求路径地址
+        if (imagePath.contains("C:" + profile)) {
+            imageUrl = imagePath.replace("C:" + profile, url + "/profile");
+        } else if (imagePath.contains(profile)) {
+            imageUrl = imagePath.replace(profile, url + "/prod-api" + "/profile");
+        }
+        // 将文件写入目标路径
+        file.transferTo(targetLocation.toFile());
+
+        // 读取测试图片
+        Mat image = Imgcodecs.imread(imagePath);
+        if (image.empty()) {
+            throw new RuntimeException("图片内存为空");
+        }
+
+        // 检测脸部
+        MatOfRect face = new MatOfRect();
+        // 检测图像中的人脸
+        faceDetector.detectMultiScale(image, face);
+        // 匹配Rect矩阵
+        Rect[] rects = face.toArray();
+        System.out.println("识别人脸个数: " + rects.length);
+        Assert.isFalse(rects.length > 1, "识别到多张人脸,请重新调整位置!");
+
+        // 识别图片中的所有人脸并分别保存
+        // int i = 1;
+        // 上面做了限制,现在其实只会有一张人脸
+        FaceCutVO faceCutVO = new FaceCutVO();
+        for (Rect rect : face.toArray()) {
+            Imgproc.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0), 3);
+            // 进行图片裁剪
+            String cutImgName = userId + "_" + currentTimestamp + "_cut.jpg";
+            content = filePath + cutImgName;
+            imageCut(imagePath, content, rect.x, rect.y, rect.width, rect.height);
+            // i++;
+            faceCutVO.setUserId(userId);
+            faceCutVO.setType("2");
+            faceCutVO.setContent(content);
+            faceCutVO.setImageUrl(imageUrl);
+            faceCutVO.setImagePath(imagePath);
+        }
+        // ---------------------以下为前端的裁剪动画展示--------------------
+        // 图片中人脸画框保存到本地
+        // Imgcodecs.imwrite("C:/work/file/test1.png", image);
+        // 展示图片
+        // HighGui.imshow("人脸识别", image);
+        // HighGui.waitKey(0);
+
+        // ---------------------------开始计算裁剪后的人脸直方图-----------------
+        Mat mat2 = conv_Mat(content); // 将路径对应的图像转换为灰度图像的Mat对象
+        if (mat2 == null) {
+            return null; // 如果转换失败,返回null
+        }
+        Mat hist2 = computeHistogram(mat2); // 计算第二个图像的直方图
+        System.out.println("hist111112--------/n" + hist2.dump());
+        faceCutVO.setMatData(MatChangeUtil.matToStr(hist2));
+        faceCutVO.setMatRows(hist2.rows());
+        faceCutVO.setMatCols(hist2.cols());
+        faceCutVO.setMatType(hist2.type());
+        return faceCutVO;
+    }
+
+    /**
+     * 裁剪人脸
+     *
+     * @param readPath 读取文件路径
+     * @param outPath  写出文件路径
+     * @param x        坐标X
+     * @param y        坐标Y
+     * @param width    截图宽度
+     * @param height   截图长度
+     */
+    public static void imageCut(String readPath, String outPath, int x, int y, int width, int height) {
+        // 原始图像
+        Mat image = Imgcodecs.imread(readPath);
+        // 截取的区域
+        Rect rect = new Rect(x, y, width, height);
+        // Mat sub = new Mat(image,rect);
+        Mat sub = image.submat(rect);
+        Mat mat = new Mat();
+        Size size = new Size(width, height);
+        // 人脸进行截图并保存
+        Imgproc.resize(sub, mat, size);
+        Imgcodecs.imwrite(outPath, mat);
+    }
+
+    /**
+     * 灰度化人脸并返回Mat对象
+     */
+    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; // 如果没有检测到人脸,返回null
+    }
+
+    /**
+     * 计算直方图
+     */
+    private static Mat computeHistogram(Mat mat) {
+        Mat hist = new Mat(); // 创建用于存储直方图的Mat对象
+        MatOfFloat ranges = new MatOfFloat(0f, 256f); // 定义直方图的范围
+        MatOfInt histSize = new MatOfInt(100); // 定义直方图的大小(越大越精确,但计算速度越慢)
+        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; // 返回计算好的直方图
+    }
+
+
+}

+ 9 - 3
ktg-common/src/main/java/com/ktg/common/utils/face/FaceMatchUtil.java

@@ -52,10 +52,17 @@ public class FaceMatchUtil
      * 人脸识别对比
      */
     public static FaceMatchVO faceRecognitionComparison(MultipartFile mfile, List<String> image2) throws IOException {
+        // 记录开始时间
+        long startTime1 = System.currentTimeMillis();
+
         Mat mat1 = conv_mat(mfile); // 转换为灰度图像的Mat对象
         Mat hist1 = computeHistogram(mat1); // 计算第一个图像的直方图
-
-        ExecutorService executorService = Executors.newFixedThreadPool(4); // 创建线程池
+        // 记录结束时间
+        long endTime1 = System.currentTimeMillis();
+        // 计算时间差(毫秒)
+        long duration1 = endTime1 - startTime1;
+        System.out.println("Execution time in milliseconds---------: " + duration1);
+        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); // 创建线程池
         List<Future<FaceMatchVO>> futures = new ArrayList<>(); // 存储Future对象的列表
 
         for (String imgPath : image2) { // 遍历第二个图像集合
@@ -75,7 +82,6 @@ public class FaceMatchUtil
         }
 
         try {
-            
             for (Future<FaceMatchVO> future : futures) { // 遍历所有Future对象
                 FaceMatchVO result = future.get(); // 获取执行结果
                 if (result != null) { // 如果结果不为空,说明找到了匹配的图像

+ 117 - 0
ktg-common/src/main/java/com/ktg/common/utils/face/FaceMatchUtil1.java

@@ -0,0 +1,117 @@
+package com.ktg.common.utils.face;
+
+
+import com.ktg.common.utils.StringUtils;
+import com.ktg.common.vo.FaceCutVO;
+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 org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 面部对比工具
+ *
+ * @author ruoyi
+ */
+public class FaceMatchUtil1
+{
+
+    @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 FaceMatchVO faceRecognitionComparison(MultipartFile mfile, List<FaceCutVO> list) throws IOException {
+        Mat mat1 = conv_mat(mfile); // 转换为灰度图像的Mat对象
+        Mat hist1 = computeHistogram(mat1); // 计算第一个图像的直方图
+
+        // ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); // 创建线程池
+        // List<Future<FaceMatchVO>> futures = new ArrayList<>(); // 存储Future对象的列表
+
+        for (FaceCutVO vo : list) { // 遍历第二个图像集合
+            // futures.add(executorService.submit(() -> {
+                Mat hist2 = MatChangeUtil.strToMat(vo.getMatData(), vo.getMatRows(), vo.getMatCols(), vo.getMatType());
+            System.out.println("hist2--------/n" + hist2.dump());
+                // Mat hist2 = computeHistogram(mat2); // 计算第二个图像的直方图
+                double v = Imgproc.compareHist(hist1, hist2, Imgproc.CV_COMP_CORREL); // 比较两个直方图的相似度
+                if (v > 0.80) { // 如果相似度大于0.80,认为匹配成功
+                    return new FaceMatchVO().setContent(vo.getContent()).setScore(v); // 返回匹配结果
+                } /*else {
+                    return null; // 否则返回null
+                }*/
+            // }));
+        }
+
+        /*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; // 如果没有检测到人脸,返回null
+    }
+
+
+    /**
+     * 计算直方图
+     */
+    private static Mat computeHistogram(Mat mat) {
+        Mat hist = new Mat(); // 创建用于存储直方图的Mat对象
+        MatOfFloat ranges = new MatOfFloat(0f, 256f); // 定义直方图的范围
+        MatOfInt histSize = new MatOfInt(100); // 定义直方图的大小(越大越精确,但计算速度越慢)
+        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; // 返回计算好的直方图
+    }
+}

+ 48 - 0
ktg-common/src/main/java/com/ktg/common/utils/face/MatChangeUtil.java

@@ -0,0 +1,48 @@
+package com.ktg.common.utils.face;
+
+import com.ktg.common.utils.StringUtils;
+import org.opencv.core.Mat;
+import org.opencv.core.MatOfByte;
+import org.opencv.imgcodecs.Imgcodecs;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.util.Base64;
+
+/**
+ *
+ *
+ * @author cgj
+ */
+public class MatChangeUtil
+{
+
+    @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);
+    }
+    public static String matToStr(Mat mat) {
+        // 将MAT对象编码为图像格式的字节数组
+        MatOfByte matOfByte = new MatOfByte();
+        Imgcodecs.imencode(".jpg", mat, matOfByte);
+        byte[] byteArray = matOfByte.toArray();
+        // 将字节数组转换为Base64编码的字符串
+        return Base64.getEncoder().encodeToString(byteArray);
+    }
+
+
+    public static Mat strToMat(String base64String, int rows, int cols, int type) {
+        // 将Base64编码的字符串解码为字节数组
+        byte[] byteArray = Base64.getDecoder().decode(base64String);
+        // 将字节数组转换为MAT对象
+        MatOfByte matOfByte = new MatOfByte(byteArray);
+        return Imgcodecs.imdecode(matOfByte, Imgcodecs.IMREAD_UNCHANGED);
+    }
+
+}

+ 12 - 0
ktg-common/src/main/java/com/ktg/common/vo/FaceCutVO.java

@@ -29,5 +29,17 @@ public class FaceCutVO implements Serializable {
     @ApiModelProperty(value = "图片路径")
     private String imagePath;
 
+    @ApiModelProperty(value = "直方图rows")
+    private Integer matRows;
+
+    @ApiModelProperty(value = "直方图cols")
+    private Integer matCols;
+
+    @ApiModelProperty(value = "直方图type")
+    private Integer matType;
+
+    @ApiModelProperty(value = "直方图数据")
+    private String matData;
+
 }
 

+ 1 - 0
ktg-framework/src/main/java/com/ktg/framework/web/service/SysLoginService.java

@@ -256,6 +256,7 @@ public class SysLoginService {
                 .eq(SysUserCharacteristic::getType, "2"));
         Assert.isFalse(list.isEmpty(), "人脸库中暂无您的人脸信息!");
         List<String> collect = list.stream().map(SysUserCharacteristic::getContent).collect(Collectors.toList());
+        // List<FaceCutVO> bean = BeanUtils.toBean(list, FaceCutVO.class);
         // 通过人脸获取最相似的用户
         // 记录开始时间
         long startTime1 = System.currentTimeMillis();

+ 11 - 0
ktg-system/src/main/java/com/ktg/system/domain/SysUserCharacteristic.java

@@ -51,5 +51,16 @@ public class SysUserCharacteristic extends BaseBean
     @ApiModelProperty(value = "删除标志(0代表存在 2代表删除)")
     private String delFlag;
 
+    @ApiModelProperty(value = "直方图rows")
+    private Integer matRows;
+
+    @ApiModelProperty(value = "直方图cols")
+    private Integer matCols;
+
+    @ApiModelProperty(value = "直方图type")
+    private Integer matType;
+
+    @ApiModelProperty(value = "直方图数据")
+    private String matData;
 
 }

+ 4 - 0
ktg-system/src/main/java/com/ktg/system/service/impl/SysUserCharacteristicServiceImpl.java

@@ -166,6 +166,10 @@ public class SysUserCharacteristicServiceImpl extends ServiceImpl<SysUserCharact
         sysUserCharacteristic.setOrderNum(0);
         sysUserCharacteristic.setImageUrl(faceCutVO.getImageUrl());
         sysUserCharacteristic.setImagePath(faceCutVO.getImagePath());
+        sysUserCharacteristic.setMatData(faceCutVO.getMatData());
+        sysUserCharacteristic.setMatRows(faceCutVO.getMatRows());
+        sysUserCharacteristic.setMatCols(faceCutVO.getMatCols());
+        sysUserCharacteristic.setMatType(faceCutVO.getMatType());
         save(sysUserCharacteristic);
         return faceCutVO;
     }