Pārlūkot izejas kodu

新增人脸分析器

车车 7 mēneši atpakaļ
vecāks
revīzija
2491515744

+ 1 - 0
ArcFace64.dat

@@ -0,0 +1 @@
+EWEPEPEOGMGTELIZJUGECKIUJDBCJTCNISGPBNHLJTJUBHEWGNAKGEGAIOHJDQAJGNCFDRFZJEDMJTGFFXHQJHCPAZJACCJJHUGIAEAWELCPAHJPDUEPAPHZHIEKBRELICAEFGBKBLBQIVAHGKICDPCFIKFOJHFSHQGPJOGXHRBWELDPBJDDAOGUFVJVCFGAIAFJCLJOIGJNDXECATHZEZACCSCYHIDRHRCACUBMAIIWFWDIIYAAFJDSERHMELACAMFPAVGCIJJQECDJAOAKDQJQEAFUDZEBGYIPATDFBPDWCMDNDYCNCLHZEHFGEVETEPCUBJBZFDJTBGEHHUIDGZBOIZEPGFFLEHCGAOHVGJIBBDIDHMFQDZIVDECXDJECIEFECLGZAVBECSCPHRJRCDGMGJINJBDLDLDDBKEZCBDMARISFJBJJFITECGQHFELIDGQGQDSIWIHIEIPCHFHIMASGUFABIFZJPDDDBAZCTIBIREPFCFXFGJJJGJSIUGJJRGJEYCPGZIUBDGLEVADHNCWGLCFIFFXCKIVGEHYHPITCCFYFCGKDBGQGMJNAZJVHGDHAGINCSCJCZGPITESEEJDHWCYCHAGDWDNIHGCCQBIGZHXBLJJFUEOHGEZJKBQEKAXFUFOERCTEMFQIOJAHOJGHKBCIBBAHMBJJTHRIVCXBJDSAQCZCOJUCAICCMFQACFEHEBCFFISBKDLFDFOELJAESFYFMCHAPIWDHBFAEBYAIFZBKJDAKGPFTDIIZDNCIHJEBCBIYGCHIBLISHZGAATAJFHGPBIIDCYATHRBXEYHMCLHMIGCWHJAAFNAVCFGVGTCPCNHRFXHHDQCUAMGWHEDKCNAVIEGVIJDWGEBCANFCHEHFHCJLGZEBBDBOIUEWHXAODGIRFUAUAKBMDBAEIIBDHJAKJGDXBDGPDWCTENEFJJFNGDIAGYEEDTBFAXGZGSAOGPANDNGIGGHJCOGDILADJIHQFBIAIZHNCLIEETFSDMGUBMCEBQICDWABDQDTGQFJAEIUGPBZGXJRIRBZACFYEJBQCYEKDVBGDYIICACFBPHZFWBNGMCAHLIFFJEICUDFGYEDEJEMBLJDDWCAFYHWJSECHXAWFNJKCAHAFLDXGRHHBZGOAXINGIFKFQFHDRIEFOIQFXBQGSGIFPIMEJFGBTAXIXFUCKDHCABUIKFZ

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

@@ -118,14 +118,21 @@ public class SysLoginController {
     @PostMapping("/loginByFingerprintDat")
     public AjaxResult loginByFingerprintDat(MultipartFile file) throws IOException {
         AjaxResult ajaxResult = loginService.loginByFingerprintDat(file);
-        // AjaxResult ajaxResult = loginService.loginByFace(file);
+        // AjaxResult ajaxResult = loginService.loginByArcFace(file);
         return ajaxResult;
     }
 
-    @ApiOperation("系统用户登录-face登录")
+    @ApiOperation("系统用户登录-face登录-opencv")
     @PostMapping("/loginByFace")
     public AjaxResult loginByFace(MultipartFile file) throws IOException {
         AjaxResult ajaxResult = loginService.loginByFace(file);
         return ajaxResult;
     }
+
+    @ApiOperation("系统用户登录-face登录-arcsoft")
+    @PostMapping("/loginByArcFace")
+    public AjaxResult loginByArcFace(MultipartFile file) throws IOException {
+        AjaxResult ajaxResult = loginService.loginByArcFace(file);
+        return ajaxResult;
+    }
 }

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

@@ -98,7 +98,7 @@ public class SysUserCharacteristicController extends BaseController
         }
         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.insertUserFace(file, userName, sysAttrValue, url));
     }
     @ApiOperation("修改用户特征(指纹、面部)")
     @PreAuthorize("@ss.hasPermi('iscs:characteristic:edit')")
@@ -127,7 +127,7 @@ public class SysUserCharacteristicController extends BaseController
         return CommonResult.success(sysUserCharacteristicService.deleteSysUserCharacteristicByRecordIds(recordIds));
     }
 
-    @ApiOperation("新增人脸录入-人脸识别后裁剪存储")
+    @ApiOperation("新增人脸录入-人脸识别后裁剪存储-opencv")
     // @PreAuthorize("@ss.hasPermi('iscs:characteristic:add')")
     @Log(title = "新增人脸录入-人脸识别后裁剪存储", businessType = BusinessType.INSERT)
     @PostMapping("/insertUserCutFace")
@@ -142,4 +142,19 @@ public class SysUserCharacteristicController extends BaseController
         return CommonResult.success(sysUserCharacteristicService.insertUserCutFace(file, userName, sysAttrValue, url));
     }
 
+    @ApiOperation("新增人脸录入-人脸识别后裁剪存储-arcsoft")
+    // @PreAuthorize("@ss.hasPermi('iscs:characteristic:add')")
+    @Log(title = "新增人脸录入-人脸识别后裁剪存储-arcsoft", businessType = BusinessType.INSERT)
+    @PostMapping("/insertUserFace")
+    public CommonResult<FaceCutVO> insertUserFace(MultipartFile file, String userName) throws IOException {
+        IsSystemAttribute one = isSystemAttributeService.getOne(Wrappers.<IsSystemAttribute>lambdaQuery()
+                .eq(IsSystemAttribute::getSysAttrKey, "sys.face.limit"));
+        String sysAttrValue = null;
+        if (one != null) {
+            sysAttrValue = one.getSysAttrValue();
+        }
+        String url = serverConfig.getUrl();
+        return CommonResult.success(sysUserCharacteristicService.insertUserFace(file, userName, sysAttrValue, url));
+    }
+
 }

+ 3 - 3
ktg-admin/src/main/resources/application-druid.yml

@@ -13,9 +13,9 @@ spring:
                 url: jdbc:mysql://36.133.174.236:3306/iscs_dev_mars?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
                 username: root
                 password: guoruan@#$devNUM1
-                #url: jdbc:mysql://100.113.202.103:3306/iscs_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
-                #username: guoruandev
-                #password: guoruan@#$devNUM1
+                #url: jdbc:mysql://192.168.28.76:3307/iscs_dev_mars?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+                #username: root
+                #password: Pm123456
             # 从库数据源
             slave:
                 # 从数据源开关/默认关闭

+ 330 - 0
ktg-common/src/main/java/com/ktg/common/utils/face/ArcSoftMothodUtil.java

@@ -0,0 +1,330 @@
+package com.ktg.common.utils.face;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.ObjectUtil;
+import com.arcsoft.face.*;
+import com.arcsoft.face.enums.DetectMode;
+import com.arcsoft.face.enums.DetectOrient;
+import com.arcsoft.face.enums.ErrorInfo;
+import com.arcsoft.face.toolkit.ImageInfo;
+import com.google.common.collect.Lists;
+import com.ktg.common.config.RuoYiConfig;
+import com.ktg.common.utils.StringUtils;
+import com.ktg.common.vo.FaceCutVO;
+import com.ktg.common.vo.FaceMatchVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;
+
+/**
+ * 面部裁剪工具
+ *
+ * @author cgj
+ */
+@Slf4j
+public class ArcSoftMothodUtil {
+
+    @Value("${ktg-mes.prod}")
+    private static String prodApi;
+
+    static FaceEngine faceEngine;
+
+    static {
+        //从官网获取
+        String appId = "5j9Uw8b5t9svFzVyVjBrCXtizjojgnjXJrNAg64UUYU4";
+        String sdkKey = "7yGfT9CQVmTrXfBmmPYeJTK3YTREQSTbM4XNVjPWzRbj";
+
+        faceEngine = new FaceEngine("C:\\work\\app\\install\\ArcSoft_ArcFace_Java_Windows_x64_V3.0\\libs\\WIN64");
+        //激活引擎
+        int errorCode = faceEngine.activeOnline(appId, sdkKey);
+
+        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
+            System.out.println("引擎激活失败");
+        }
+
+
+        ActiveFileInfo activeFileInfo = new ActiveFileInfo();
+        errorCode = faceEngine.getActiveFileInfo(activeFileInfo);
+        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
+            System.out.println("获取激活文件信息失败");
+        }
+
+        //引擎配置
+        EngineConfiguration engineConfiguration = new EngineConfiguration();
+        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
+        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
+        engineConfiguration.setDetectFaceMaxNum(10);
+        engineConfiguration.setDetectFaceScaleVal(16);
+        //功能配置
+        FunctionConfiguration functionConfiguration = new FunctionConfiguration();
+        functionConfiguration.setSupportAge(true);
+        functionConfiguration.setSupportFace3dAngle(true);
+        functionConfiguration.setSupportFaceDetect(true);
+        functionConfiguration.setSupportFaceRecognition(true);
+        functionConfiguration.setSupportGender(true);
+        functionConfiguration.setSupportLiveness(true);
+        functionConfiguration.setSupportIRLiveness(true);
+        engineConfiguration.setFunctionConfiguration(functionConfiguration);
+
+        //初始化引擎
+        errorCode = faceEngine.init(engineConfiguration);
+
+        if (errorCode != ErrorInfo.MOK.getValue()) {
+            System.out.println("初始化引擎失败");
+        }
+    }
+
+
+    /**
+     * 图片人脸检测
+     */
+    public static FaceCutVO saveArcData(MultipartFile file, Long userId, String url) throws IOException {
+        int errorCode;
+        // 1.判断这个文件是否有效
+        Assert.isFalse(file.isEmpty(), "请上传人脸文件!");
+        // 2.-----------------开始存储上传的照片--------------------
+        // 人脸存储基础路径
+        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());
+
+        // 3--------------------------开始读取照片的特征值-------------------------------
+        ImageInfo imageInfo = getRGBData(new File(imagePath));
+        List<FaceInfo> faceInfoList = new ArrayList<>();
+        errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
+        Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "获取人脸失败!");
+
+        //特征提取
+        FaceFeature faceFeature = new FaceFeature();
+        errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);
+        Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "获取人脸特征值失败!");
+        byte[] featureData = faceFeature.getFeatureData();
+        // 4------------------------返回解析的数据---------------------
+        FaceCutVO faceCutVO = new FaceCutVO();
+        faceCutVO.setType("2");
+        faceCutVO.setContent(Arrays.toString(featureData));
+        faceCutVO.setImageUrl(imageUrl);
+        faceCutVO.setImagePath(imagePath);
+        return faceCutVO;
+    }
+
+    public static void main(String[] args) throws UnsupportedEncodingException {
+
+
+        /*String strWithCharset = new String(bytes, StandardCharsets.UTF_8); // 使用默认字符集
+        System.out.println(bytes);
+        System.out.println(strWithCharset);
+        byte[] bytes1 = strWithCharset.getBytes(StandardCharsets.UTF_8);
+        for (byte b : bytes1) {
+            System.out.print(b + " ");
+        }*/
+        byte[] bytes = {72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33};
+        String string = Arrays.toString(bytes);
+
+        // Remove the brackets and spaces from the string
+        string = string.substring(1, string.length() - 1).replaceAll("\\s", "");
+
+        // Split the string by commas to get each number as a string
+        String[] numbers = string.split(",");
+        // Create a new byte array with the same length as the original
+        byte[] newBytes = new byte[numbers.length];
+
+        // Convert each string number back to a byte and store it in the new array
+        for (int i = 0; i < numbers.length; i++) {
+            newBytes[i] = Byte.parseByte(numbers[i]);
+        }
+        // Print the new byte array to verify it matches the original
+    }
+
+    // 相似度门槛
+    private static final double THRESHOLD = 0.8; // THRESHOLD越高,错误率越低,阈值[0,1]
+    private static final ExecutorService THREAD_POOL_EXECUTOR = Executors.newFixedThreadPool(4); // 线程池
+
+    public static FaceMatchVO completableFutureComparison(final MultipartFile file, final List<String> matcher) throws IOException {
+        int errorCode;
+        // 提取当前人脸的特征值,比对文件零时存储,人脸存储基础路径
+        String profile = RuoYiConfig.getProfile();
+        String basePath = profile + "/face/" + 0 + "/";
+        if (StringUtils.isBlank(prodApi)) {
+            basePath = "C:" + basePath;
+        }
+        // 时间戳
+        long currentTimestamp = System.currentTimeMillis();
+        String filePath = basePath;
+        // 确保目录存在,不存在则创建一个
+        Path uploadPath = Paths.get(filePath);
+        if (!Files.exists(uploadPath)) {
+            Files.createDirectories(uploadPath);
+        }
+        // 获取文件名并构建目标路径
+        String fileName = filePath + currentTimestamp + "_" + file.getOriginalFilename();
+        Path targetLocation = uploadPath.resolve(fileName);
+        // 将文件写入目标路径
+        file.transferTo(targetLocation.toFile());
+        try {
+            //人脸检测
+            ImageInfo imageInfo = getRGBData(new File(fileName));
+            List<FaceInfo> faceInfoList = new ArrayList<>();
+            errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
+            Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "未识别到人脸!");
+
+            //特征提取
+            FaceFeature faceFeature = new FaceFeature();
+            errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);
+            Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "获取人脸特征值失败!");
+            byte[] featureData = faceFeature.getFeatureData();
+
+            // 转成list
+            List<String> matcherFeatureDataList = new ArrayList<>(matcher);
+            /*FaceFeature targetFaceFeature = new FaceFeature();
+            targetFaceFeature.setFeatureData(featureData);
+            FaceFeature sourceFaceFeature = new FaceFeature();
+            FaceSimilar faceSimilar = new FaceSimilar();
+            for (String matcherData : matcherFeatureDataList) {
+                // 输入的人脸和人脸库中的人脸进行对比,找到当前线程中的分数最高的人脸
+                byte[] matchData = strToBytes(matcherData);
+                //特征比对
+                sourceFaceFeature.setFeatureData(matchData);
+                int errorCode1 = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
+                Assert.isFalse(errorCode1 != ErrorInfo.MOK.getValue(), "人脸对比异常" + errorCode1 + "!");
+                // 相似度
+                float score = faceSimilar.getScore();
+                if (score > THRESHOLD) {
+                    return new FaceMatchVO().setContent(matcherData).setScore((double) score);
+                }
+            }*/
+
+            // 切分四等份
+            int denominator = 1;
+            if (matcherFeatureDataList.size() >= 4) {
+                denominator = 4;
+            }
+            List<List<String>> averageMatcherFeatureDataList = Lists.partition(matcherFeatureDataList, matcherFeatureDataList.size() / denominator);
+
+            // 构建四个线程进行处理,防止人员过多对比速度太慢
+            CompletableFuture<FaceMatchVO>[] completableFutureArray = averageMatcherFeatureDataList.stream().map(
+                    partitionFeatureData -> CompletableFuture.supplyAsync(
+                            () -> comparison(featureData, new ArrayList<>(partitionFeatureData)), THREAD_POOL_EXECUTOR)
+            ).toArray(CompletableFuture[]::new);
+            // 等待所有任务执行完
+            CompletableFuture.allOf(completableFutureArray).join();
+            List<FaceMatchVO> verificationList = new ArrayList<>();
+            for (CompletableFuture<FaceMatchVO> completableFuture : completableFutureArray) {
+                try {
+                    FaceMatchVO verification = completableFuture.get();
+                    if (ObjectUtil.isNotEmpty(verification)) {
+                        verificationList.add(verification);
+                    }
+                } catch (InterruptedException | ExecutionException e) {
+                    log.error(e.getMessage(), e);
+                }
+            }
+            // 找出最匹配的人脸(匹配度最高)
+            if (ObjectUtil.isNotEmpty(verificationList)) {
+                FaceMatchVO max = verificationList.stream().max(Comparator.comparing(FaceMatchVO::getScore)).get();
+                log.info("相似值最佳的的人脸:{},分数{}", max.getContent(), max.getScore());
+                return max;
+            }
+        } catch (Exception e) {
+            log.error("人脸比对异常:{}", e.getMessage());
+        }
+        return null;
+    }
+
+    public static FaceMatchVO comparison(final byte[] featureData, final List<String> matcherFeatureDataList) {
+        try {
+            if (!matcherFeatureDataList.isEmpty()) {
+                // 最匹配的人脸
+                String match = null;
+                // 峰值
+                double high = 0;
+                FaceFeature targetFaceFeature = new FaceFeature();
+                targetFaceFeature.setFeatureData(featureData);
+                FaceFeature sourceFaceFeature = new FaceFeature();
+                FaceSimilar faceSimilar = new FaceSimilar();
+                for (String matcherData : matcherFeatureDataList) {
+                    // 输入的人脸和人脸库中的人脸进行对比,找到当前线程中的分数最高的人脸
+                    byte[] matchData = strToBytes(matcherData);
+                    //特征比对
+                    sourceFaceFeature.setFeatureData(matchData);
+                    int errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
+                    Assert.isFalse(errorCode != ErrorInfo.MOK.getValue(), "人脸对比异常" + errorCode + "!");
+                    // 相似度
+                    float score = faceSimilar.getScore();
+                    if (score > high) {
+                        high = score;
+                        match = matcherData;
+                    }
+                }
+                return high >= THRESHOLD ? new FaceMatchVO().setContent(match).setScore(high) : null;
+            }
+        } catch (Exception e) {
+            log.error("人脸比对异常:{}", e.getMessage());
+        }
+        return null;
+    }
+
+    private static byte[] strToBytes(String string) {
+        // Remove the brackets and spaces from the string
+        string = string.substring(1, string.length() - 1).replaceAll("\\s", "");
+        // Split the string by commas to get each number as a string
+        String[] numbers = string.split(",");
+        // Create a new byte array with the same length as the original
+        byte[] newBytes = new byte[numbers.length];
+        // Convert each string number back to a byte and store it in the new array
+        for (int i = 0; i < numbers.length; i++) {
+            newBytes[i] = Byte.parseByte(numbers[i]);
+        }
+        // Print the new byte array to verify it matches the original
+        // System.out.println("New byte array: " + Arrays.toString(newBytes));
+        return newBytes;
+    }
+
+}

+ 172 - 0
ktg-common/src/main/java/com/ktg/common/utils/face/FaceEngineTest.java

@@ -0,0 +1,172 @@
+package com.ktg.common.utils.face;
+
+import com.arcsoft.face.*;
+import com.arcsoft.face.enums.DetectMode;
+import com.arcsoft.face.enums.DetectModel;
+import com.arcsoft.face.enums.DetectOrient;
+import com.arcsoft.face.enums.ErrorInfo;
+import com.arcsoft.face.toolkit.ImageInfo;
+import com.arcsoft.face.toolkit.ImageInfoEx;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.arcsoft.face.toolkit.ImageFactory.getGrayData;
+import static com.arcsoft.face.toolkit.ImageFactory.getRGBData;
+
+public class FaceEngineTest {
+
+
+    public static void main(String[] args) {
+
+        //从官网获取
+        String appId = "5j9Uw8b5t9svFzVyVjBrCXtizjojgnjXJrNAg64UUYU4";
+        String sdkKey = "7yGfT9CQVmTrXfBmmPYeJTK3YTREQSTbM4XNVjPWzRbj";
+
+
+        FaceEngine faceEngine = new FaceEngine("C:\\work\\app\\install\\ArcSoft_ArcFace_Java_Windows_x64_V3.0\\libs\\WIN64");
+        //激活引擎
+        int errorCode = faceEngine.activeOnline(appId, sdkKey);
+
+        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
+            System.out.println("引擎激活失败");
+        }
+
+
+        ActiveFileInfo activeFileInfo=new ActiveFileInfo();
+        errorCode = faceEngine.getActiveFileInfo(activeFileInfo);
+        if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
+            System.out.println("获取激活文件信息失败");
+        }
+
+        //引擎配置
+        EngineConfiguration engineConfiguration = new EngineConfiguration();
+        engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
+        engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
+        engineConfiguration.setDetectFaceMaxNum(10);
+        engineConfiguration.setDetectFaceScaleVal(16);
+        //功能配置
+        FunctionConfiguration functionConfiguration = new FunctionConfiguration();
+        functionConfiguration.setSupportAge(true);
+        functionConfiguration.setSupportFace3dAngle(true);
+        functionConfiguration.setSupportFaceDetect(true);
+        functionConfiguration.setSupportFaceRecognition(true);
+        functionConfiguration.setSupportGender(true);
+        functionConfiguration.setSupportLiveness(true);
+        functionConfiguration.setSupportIRLiveness(true);
+        engineConfiguration.setFunctionConfiguration(functionConfiguration);
+
+
+        //初始化引擎
+        errorCode = faceEngine.init(engineConfiguration);
+
+        if (errorCode != ErrorInfo.MOK.getValue()) {
+            System.out.println("初始化引擎失败");
+        }
+
+
+        //人脸检测
+        ImageInfo imageInfo = getRGBData(new File("d:\\aaa.jpg"));
+        List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
+        errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList);
+        System.out.println(faceInfoList);
+
+        //特征提取
+        FaceFeature faceFeature = new FaceFeature();
+        errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature);
+        System.out.println("特征值大小:" + faceFeature.getFeatureData().length);
+
+        //人脸检测2
+        ImageInfo imageInfo2 = getRGBData(new File("d:\\ccc.jpg"));
+        List<FaceInfo> faceInfoList2 = new ArrayList<FaceInfo>();
+        errorCode = faceEngine.detectFaces(imageInfo2.getImageData(), imageInfo2.getWidth(), imageInfo2.getHeight(),imageInfo2.getImageFormat(), faceInfoList2);
+        System.out.println(faceInfoList2);
+
+        //特征提取2
+        FaceFeature faceFeature2 = new FaceFeature();
+        errorCode = faceEngine.extractFaceFeature(imageInfo2.getImageData(), imageInfo2.getWidth(), imageInfo2.getHeight(), imageInfo2.getImageFormat(), faceInfoList2.get(0), faceFeature2);
+        System.out.println("特征值大小:" + faceFeature2.getFeatureData().length);
+
+        //特征比对
+        FaceFeature targetFaceFeature = new FaceFeature();
+        targetFaceFeature.setFeatureData(faceFeature.getFeatureData());
+        FaceFeature sourceFaceFeature = new FaceFeature();
+        sourceFaceFeature.setFeatureData(faceFeature2.getFeatureData());
+        FaceSimilar faceSimilar = new FaceSimilar();
+
+        errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
+
+        System.out.println("相似度:" + faceSimilar.getScore());
+
+        //设置活体测试
+        errorCode = faceEngine.setLivenessParam(0.5f, 0.7f);
+        //人脸属性检测
+        FunctionConfiguration configuration = new FunctionConfiguration();
+        configuration.setSupportAge(true);
+        configuration.setSupportFace3dAngle(true);
+        configuration.setSupportGender(true);
+        configuration.setSupportLiveness(true);
+        errorCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList, configuration);
+
+
+        //性别检测
+        List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();
+        errorCode = faceEngine.getGender(genderInfoList);
+        System.out.println("性别:" + genderInfoList.get(0).getGender());
+
+        //年龄检测
+        List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();
+        errorCode = faceEngine.getAge(ageInfoList);
+        System.out.println("年龄:" + ageInfoList.get(0).getAge());
+
+        //3D信息检测
+        List<Face3DAngle> face3DAngleList = new ArrayList<Face3DAngle>();
+        errorCode = faceEngine.getFace3DAngle(face3DAngleList);
+        System.out.println("3D角度:" + face3DAngleList.get(0).getPitch() + "," + face3DAngleList.get(0).getRoll() + "," + face3DAngleList.get(0).getYaw());
+
+        //活体检测
+        List<LivenessInfo> livenessInfoList = new ArrayList<LivenessInfo>();
+        errorCode = faceEngine.getLiveness(livenessInfoList);
+        System.out.println("活体:" + livenessInfoList.get(0).getLiveness());
+
+
+        //IR属性处理
+        ImageInfo imageInfoGray = getGrayData(new File("d:\\IR_480p.jpg"));
+        List<FaceInfo> faceInfoListGray = new ArrayList<FaceInfo>();
+        errorCode = faceEngine.detectFaces(imageInfoGray.getImageData(), imageInfoGray.getWidth(), imageInfoGray.getHeight(), imageInfoGray.getImageFormat(), faceInfoListGray);
+
+        FunctionConfiguration configuration2 = new FunctionConfiguration();
+        configuration2.setSupportIRLiveness(true);
+        errorCode = faceEngine.processIr(imageInfoGray.getImageData(), imageInfoGray.getWidth(), imageInfoGray.getHeight(), imageInfoGray.getImageFormat(), faceInfoListGray, configuration2);
+        //IR活体检测
+        List<IrLivenessInfo> irLivenessInfo = new ArrayList<>();
+        errorCode = faceEngine.getLivenessIr(irLivenessInfo);
+        System.out.println("IR活体:" + irLivenessInfo.get(0).getLiveness());
+
+        ImageInfoEx imageInfoEx = new ImageInfoEx();
+        imageInfoEx.setHeight(imageInfo.getHeight());
+        imageInfoEx.setWidth(imageInfo.getWidth());
+        imageInfoEx.setImageFormat(imageInfo.getImageFormat());
+        imageInfoEx.setImageDataPlanes(new byte[][]{imageInfo.getImageData()});
+        imageInfoEx.setImageStrides(new int[]{imageInfo.getWidth() * 3});
+        List<FaceInfo> faceInfoList1 = new ArrayList<>();
+        errorCode = faceEngine.detectFaces(imageInfoEx, DetectModel.ASF_DETECT_MODEL_RGB, faceInfoList1);
+
+        FunctionConfiguration fun = new FunctionConfiguration();
+        fun.setSupportAge(true);
+        errorCode = faceEngine.process(imageInfoEx, faceInfoList1, functionConfiguration);
+        List<AgeInfo> ageInfoList1 = new ArrayList<>();
+        int age = faceEngine.getAge(ageInfoList1);
+        System.out.println("年龄:" + ageInfoList1.get(0).getAge());
+
+        FaceFeature feature = new FaceFeature();
+        errorCode = faceEngine.extractFaceFeature(imageInfoEx, faceInfoList1.get(0), feature);
+
+
+        //引擎卸载
+        //errorCode = faceEngine.unInit();
+
+    }
+}
+

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

@@ -14,6 +14,7 @@ import com.ktg.common.exception.user.CaptchaException;
 import com.ktg.common.exception.user.CaptchaExpireException;
 import com.ktg.common.exception.user.UserPasswordNotMatchException;
 import com.ktg.common.utils.*;
+import com.ktg.common.utils.face.ArcSoftMothodUtil;
 import com.ktg.common.utils.face.FaceMatchUtil;
 import com.ktg.common.utils.ip.IpUtils;
 import com.ktg.common.vo.FaceMatchVO;
@@ -281,5 +282,47 @@ public class SysLoginService {
         return ajax;
     }
 
+    public AjaxResult loginByArcFace(MultipartFile file) throws IOException {
+
+        System.out.println("Execution time in milliseconds进来了-----: ");
+        // 记录开始时间
+        long startTime1 = System.currentTimeMillis();
+        Assert.isTrue(file.getSize() > 0, "输入的人脸不能为空!");
+        // 获取所有的用户人脸
+        List<SysUserCharacteristic> list = iSysUserCharacteristicService.list(Wrappers.<SysUserCharacteristic>lambdaQuery()
+                .eq(SysUserCharacteristic::getType, "2"));
+        Assert.isFalse(list.isEmpty(), "人脸库中暂无您的人脸信息!");
+        List<String> collect = list.stream().map(SysUserCharacteristic::getContent).collect(Collectors.toList());
+        // 通过人脸获取最相似的用户
+        FaceMatchVO faceMatchVO = ArcSoftMothodUtil.completableFutureComparison(file, collect);
+        // FaceMatchVO faceMatchVO = ArcSoftMothodUtil.completableFutureComparison(file, new HashSet<>(collect));
+        // 记录结束时间
+        long endTime1 = System.currentTimeMillis();
+        // 计算时间差(毫秒)
+        long duration1 = endTime1 - startTime1;
+        System.out.println("Execution time in milliseconds-----: " + duration1);
+        Assert.notNull(faceMatchVO, "无法根据人脸确定您的身份,请通过其它方式登录!");
+        // 记录开始时间
+        long startTime2 = System.currentTimeMillis();
+        String okFace = faceMatchVO.getContent();
+        SysUserCharacteristic one = iSysUserCharacteristicService.getOne(Wrappers.<SysUserCharacteristic>lambdaQuery()
+                .eq(SysUserCharacteristic::getContent, okFace)
+                .last("limit 1"));
+        SysUser sysUser = iSysUserService.getById(one.getUserId());
+        // 生成令牌
+        AjaxResult ajax = AjaxResult.success();
+        String token = loginWithoutPassword(sysUser);
+        ajax.put(Constants.TOKEN, token);
+        ajax.put("nickName", sysUser.getNickName());
+
+        // 记录结束时间
+        long endTime2 = System.currentTimeMillis();
+        // 计算时间差(毫秒)
+        long duration2 = endTime2 - startTime2;
+        System.out.println("Execution time in milliseconds2-----: " + duration2);
+
+        return ajax;
+    }
+
 
 }

+ 32 - 24
ktg-iscs/src/main/resources/mapper/IsExceptionMapper.xml

@@ -5,28 +5,28 @@
 <mapper namespace="com.ktg.iscs.mapper.IsExceptionMapper">
 
     <select id="getIsExceptionPage" resultType="com.ktg.iscs.domain.IsException">
+
+        select a.* from (
         SELECT
-            e.*,
-            d.dict_label AS exception_category_name,
-            CASE WHEN e.exception_category = 1 THEN l.loto_name WHEN e.exception_category = 2 THEN m.cabinet_name END AS source_name,
-            dd.dict_label AS exception_type_name,
-            ddd.dict_label AS exception_level_name,
-            u.nick_name AS raiser_name
+        e.*,
+        d.dict_label AS exception_category_name,
+        CASE WHEN e.exception_category = 1 THEN l.loto_name WHEN e.exception_category = 2 THEN m.cabinet_name END AS source_name,
+        dd.dict_label AS exception_type_name,
+        ddd.dict_label AS exception_level_name,
+        u.nick_name AS raiser_name
         FROM
-            is_exception e
-            LEFT JOIN sys_dict_data d ON e.exception_category = d.dict_value AND d.dict_type = 'classification_of_exceptions'
-            LEFT JOIN is_loto_station l ON l.loto_id = e.source_id
-            LEFT JOIN is_materials_cabinet m ON m.cabinet_id = e.source_id
-            LEFT JOIN sys_dict_data dd ON e.exception_category = dd.dict_value AND dd.dict_type = 'type_of_exception'
-            LEFT JOIN sys_dict_data ddd ON e.exception_category = ddd.dict_value AND ddd.dict_type = 'severity_level'
-            LEFT JOIN sys_user u ON u.user_id = e.raiser
+        is_exception e
+        LEFT JOIN sys_dict_data d ON e.exception_category = d.dict_value AND d.dict_type =
+        'classification_of_exceptions'
+        LEFT JOIN is_loto_station l ON l.loto_id = e.source_id
+        LEFT JOIN is_materials_cabinet m ON m.cabinet_id = e.source_id
+        LEFT JOIN sys_dict_data dd ON e.exception_type = dd.dict_value AND dd.dict_type = 'type_of_exception'
+        LEFT JOIN sys_dict_data ddd ON e.exception_level = ddd.dict_value AND ddd.dict_type = 'severity_level'
+        LEFT JOIN sys_user u ON u.user_id = e.raiser
         <where>
             <if test="isException.exceptionCategory != null and isException.exceptionCategory.trim != ''">
                 and e.exception_category = #{isException.exceptionCategory}
             </if>
-            <if test="isException.sourceName != null and isException.sourceName.trim != ''">
-                and (l.loto_name like concat('%',#{isException.sourceName},'%') or m.cabinet_name concat('%',#{isException.sourceName},'%'))
-            </if>
             <if test="isException.exceptionType != null and isException.exceptionType.trim != ''">
                 and e.exception_type = #{isException.exceptionType}
             </if>
@@ -48,8 +48,16 @@
             <if test="isException.endHandleTime != null and isException.endHandleTime.trim != ''">
                 and e.handle_time &lt;= #{isException.endHandleTime}
             </if>
-        </where>
-        order by e.exception_id desc
+            <if test="isException.status != null and isException.status.trim != ''">
+                and e.status = #{isException.status}
+            </if>
+        </where>) a
+                   <where>
+                       <if test="isException.sourceName != null and isException.sourceName.trim != ''">
+                           and a.source_name like concat('%',#{isException.sourceName},'%')
+                       </if>
+                   </where>
+        order by a.exception_id desc
     </select>
     <select id="selectIsExceptionById" resultType="com.ktg.iscs.domain.IsException">
         SELECT
@@ -61,12 +69,12 @@
             u.nick_name AS raiser_name
         FROM
             is_exception e
-            LEFT JOIN sys_dict_data d ON e.exception_category = d.dict_value AND d.dict_type = 'classification_of_exceptions'
-            LEFT JOIN is_loto_station l ON l.loto_id = e.source_id
-            LEFT JOIN is_materials_cabinet m ON m.cabinet_id = e.source_id
-            LEFT JOIN sys_dict_data dd ON e.exception_category = dd.dict_value AND dd.dict_type = 'type_of_exception'
-            LEFT JOIN sys_dict_data ddd ON e.exception_category = ddd.dict_value AND ddd.dict_type = 'severity_level'
-            LEFT JOIN sys_user u ON u.user_id = e.raiser
+                LEFT JOIN sys_dict_data d ON e.exception_category = d.dict_value AND d.dict_type = 'classification_of_exceptions'
+                LEFT JOIN is_loto_station l ON l.loto_id = e.source_id
+                LEFT JOIN is_materials_cabinet m ON m.cabinet_id = e.source_id
+                LEFT JOIN sys_dict_data dd ON e.exception_type = dd.dict_value AND dd.dict_type = 'type_of_exception'
+                LEFT JOIN sys_dict_data ddd ON e.exception_level = ddd.dict_value AND ddd.dict_type = 'severity_level'
+                LEFT JOIN sys_user u ON u.user_id = e.raiser
         where e.exception_id = #{exceptionId}
     </select>
 </mapper>

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

@@ -1,12 +1,12 @@
 package com.ktg.system.domain;
 
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import io.swagger.annotations.ApiModelProperty;
-import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
 import com.ktg.common.annotation.Excel;
 import com.ktg.common.core.domain.model.BaseBean;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
 
 /**
  * 用户特征(指纹、面部)对象 sys_user_characteristic

+ 1 - 0
ktg-system/src/main/java/com/ktg/system/service/ISysUserCharacteristicService.java

@@ -23,6 +23,7 @@ public interface ISysUserCharacteristicService extends IService<SysUserCharacter
 
     FaceCutVO insertUserCutFace(MultipartFile file, String userName, String sysAttrValue, String url) throws IOException;
 
+    FaceCutVO insertUserFace(MultipartFile file, String userName, String sysAttrValue, String url) throws IOException;
 
     Boolean deleteSysUserCharacteristicByRecordIds(String recordIds);
 

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

@@ -9,6 +9,7 @@ import com.ktg.common.config.RuoYiConfig;
 import com.ktg.common.core.domain.entity.SysUser;
 import com.ktg.common.utils.StringUtils;
 import com.ktg.common.utils.face.FaceCutUtil;
+import com.ktg.common.utils.face.ArcSoftMothodUtil;
 import com.ktg.common.vo.FaceCutVO;
 import com.ktg.system.domain.SysUserCharacteristic;
 import com.ktg.system.mapper.SysUserCharacteristicMapper;
@@ -174,6 +175,33 @@ public class SysUserCharacteristicServiceImpl extends ServiceImpl<SysUserCharact
         return faceCutVO;
     }
 
+    @Override
+    public FaceCutVO insertUserFace(MultipartFile file, String userName, String sysAttrValue, String url) throws IOException {
+        Assert.notBlank(userName, "请告知我这是哪个用户的人脸!");
+        Assert.isTrue(file.getSize() > 0, "人脸信息不能为空!");
+        SysUser user = iSysUserService.getOne(Wrappers.<SysUser>lambdaQuery()
+                .eq(SysUser::getUserName, userName));
+        Assert.isFalse(user == null, "系统中无该用户!");
+        Long userId = user.getUserId();
+        if (StringUtils.isNotBlank(sysAttrValue)) {
+            // 检查人员指纹上限
+            long count = count(Wrappers.<SysUserCharacteristic>lambdaQuery()
+                    .eq(SysUserCharacteristic::getUserId, userId));
+            int i = Integer.parseInt(sysAttrValue);
+            Assert.isFalse(count >= i, "该人员的人脸录入已上限,最大"+ i + "条!");
+        }
+        FaceCutVO faceCutVO = ArcSoftMothodUtil.saveArcData(file, userId, url);
+        SysUserCharacteristic sysUserCharacteristic = new SysUserCharacteristic();
+        sysUserCharacteristic.setUserId(userId);
+        sysUserCharacteristic.setType("2");
+        sysUserCharacteristic.setContent(faceCutVO.getContent());
+        sysUserCharacteristic.setOrderNum(0);
+        sysUserCharacteristic.setImageUrl(faceCutVO.getImageUrl());
+        sysUserCharacteristic.setImagePath(faceCutVO.getImagePath());
+        save(sysUserCharacteristic);
+        return faceCutVO;
+    }
+
     @Override
     public Boolean deleteSysUserCharacteristicByRecordIds(String recordIds) {
         Assert.notBlank(recordIds, "请选择需要删除的数据!");