车车 8 сар өмнө
parent
commit
4c92044876

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

@@ -0,0 +1,160 @@
+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;
+
+/**
+ * 面部裁剪工具
+ *
+ * @author cgj
+ */
+public class FaceCutUtil
+{
+
+    @Value("${ktg-mes.prod}")
+    private static String prodApi;
+
+    public static void main(String[] args) throws IOException {
+        imageFaceDetection(null, 1L, null);
+    }
+
+    // 初始化人脸探测器
+    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);
+        // System.loadLibrary("C:/work/app/install/opencv/build/java/opencv-460.jar");
+        // 从配置文件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";
+            // faceDetectorPath = "C:\\work\\app\\install\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml";
+        }
+        // CascadeClassifier faceDetector = new CascadeClassifier(faceDetectorPath);
+        String property = "C:/work/app/install/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
+        System.out.println(property);
+        faceDetector = new CascadeClassifier(property);
+
+        // -------------------- 定义文件的基础属性----------------------------
+        // 人脸存储基础路径
+        String profile = RuoYiConfig.getProfile();
+        String basePath = profile + "/face/" + userId +"/";
+        if (StringUtils.isBlank(prodApi)) {
+            basePath = "C:" + basePath;
+        }
+        // 原文件裁剪后的路径
+        String content;
+        // 原文件转存后的路径
+        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 + "_cut_" + currentTimestamp + ".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);
+
+        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);
+    }
+}

+ 102 - 0
ktg-common/src/main/java/com/ktg/common/utils/face/FaceMatchUtil.java

@@ -0,0 +1,102 @@
+package com.ktg.common.utils.face;
+
+
+import com.ktg.common.utils.StringUtils;
+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 FaceMatchUtil
+{
+    // 初始化人脸探测器
+    static CascadeClassifier faceDetector;
+
+    @Value("${ktg-mes.prod}")
+    private static String prodApi;
+
+    static {
+        // 加载OpenCV本地库
+        // System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+        String loadPath = "/guoruan/app/opencv/build/java/x64/opencv_java460.dll";
+        if (StringUtils.isBlank(prodApi)) {
+            loadPath = "C:/work/app/install/opencv/build/java/x64/opencv_java460.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);
+    }
+
+    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 double 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);
+
+        // 比较两个密集或两个稀疏直方图
+        return Imgproc.compareHist(mat3, mat4, Imgproc.CV_COMP_CORREL);
+    }
+
+    /**
+     * 灰度化人脸
+     */
+    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;
+    }
+}

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

@@ -0,0 +1,33 @@
+package com.ktg.common.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+@EqualsAndHashCode(callSuper = false)
+@Data
+@Accessors(chain = true)
+public class FaceCutVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "用户id")
+    private Long userId;
+
+    @ApiModelProperty(value = "类型(1-指纹图片 2-面部图片)")
+    private String type;
+
+    @ApiModelProperty(value = "内容")
+    private String content;
+
+    @ApiModelProperty(value = "图片地址")
+    private String imageUrl;
+
+    @ApiModelProperty(value = "图片路径")
+    private String imagePath;
+
+}
+