Quellcode durchsuchen

人脸上传,比对接口

wujiefeng vor 1 Jahr
Ursprung
Commit
fef65bc52f

+ 2 - 0
centers/AuthCenter/AuthClient/src/main/java/com/github/microservice/auth/client/content/ResultState.java

@@ -48,6 +48,8 @@ public enum ResultState {
 
     FaceNotExists("人脸图片不存在"),
     FaceNoMatches("无匹配的人脸"),
+    FaceVectorSetFail("人脸库写入失败"),
+    FaceDataDeleteFail("人脸库删除失败"),
 
     NFCCardNotExists("NFC卡号不存在"),
     NFCCardExists("NFC卡号已存在"),

+ 4 - 2
centers/AuthCenter/AuthClient/src/main/java/com/github/microservice/auth/client/model/UserFaceUploadModel.java

@@ -19,8 +19,10 @@ public class UserFaceUploadModel {
 //    @NotNull(message = "userId不能为null")
     private String userId;
 
-    @Schema(description = "人脸图片")
-    private MultipartFile file;
+//    @Schema(description = "人脸图片")
+//    private MultipartFile file;
+
+    private String file;
 
 //    @Schema(description = "对比人脸照片")
 //    @NotNull(message = "compareFace不能为null")

+ 0 - 43
centers/AuthCenter/AuthServer/src/main/java/com/github/microservice/auth/server/core/conf/RedisearchConf.java

@@ -1,43 +0,0 @@
-package com.github.microservice.auth.server.core.conf;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-@Component
-@ConfigurationProperties(prefix = "redisearch")
-public class RedisearchConf {
-
-    /**/
-    private String host;
-
-    private int port;
-
-    private String username;
-
-    private String password;
-
-
-    /*******************索引参数***********************/
-
-    /*索引名*/
-    private String indexName;
-
-    /*索引字段*/
-    private String fieldName;
-
-    /*索引key前缀*/
-    private String prefix;
-
-    /*向量维度*/
-    private String dim;
-
-    /*相似度*/
-    private String score;
-}

+ 13 - 13
centers/AuthCenter/AuthServer/src/main/java/com/github/microservice/auth/server/core/controller/user/UserController.java

@@ -38,14 +38,14 @@ public class UserController {
         return this.userService.refreshToken(refreshToken);
     }
 
-    @Operation(summary = "上传人脸", description = "上传人脸")
-    @RequestMapping(value = "uploadFace", method = {RequestMethod.POST})
-    public ResultContent<String> upload(UserFaceUploadModel userFaceUploadModel){
-        ResultContent<String> add = userService.add(UserAuthModel.builder().loginValue("face:" + UUID.randomUUID().toString()).loginType(LoginType.Phone).passWord(UUID.randomUUID().toString()).build());
-        String userId = add.getContent();
-        userFaceUploadModel.setUserId(userId);
-        return userFaceService.upload(userFaceUploadModel);
-    }
+//    @Operation(summary = "上传人脸", description = "上传人脸")
+//    @RequestMapping(value = "uploadFace", method = {RequestMethod.POST})
+//    public ResultContent<String> upload(UserFaceUploadModel userFaceUploadModel){
+//        ResultContent<String> add = userService.add(UserAuthModel.builder().loginValue("face:" + UUID.randomUUID().toString()).loginType(LoginType.Phone).passWord(UUID.randomUUID().toString()).build());
+//        String userId = add.getContent();
+//        userFaceUploadModel.setUserId(userId);
+//        return userFaceService.upload(userFaceUploadModel);
+//    }
 
     @Operation(summary = "比对人脸", description = "比对人脸")
     @RequestMapping(value = "matchesFace", method = {RequestMethod.POST})
@@ -53,9 +53,9 @@ public class UserController {
         return userFaceService.matches(file);
     }
 
-    @Operation(summary = "删除人脸", description = "删除人脸")
-    @RequestMapping(value = "deleteFace", method = {RequestMethod.GET})
-    public ResultContent deleteFace(@RequestParam("userId") String userId, @RequestParam("faceFileId") String faceFileId){
-        return userFaceService.delete(userId, faceFileId);
-    }
+//    @Operation(summary = "删除人脸", description = "删除人脸")
+//    @RequestMapping(value = "deleteFace", method = {RequestMethod.GET})
+//    public ResultContent deleteFace(@RequestParam("userId") String userId, @RequestParam("faceFileId") String faceFileId){
+//        return userFaceService.delete(userId, faceFileId);
+//    }
 }

+ 1 - 1
centers/AuthCenter/AuthServer/src/main/java/com/github/microservice/auth/server/core/domain/UserFace.java

@@ -24,7 +24,7 @@ public class UserFace extends SuperEntity {
     private String faceFSId;
 
     //人脸向量
-    private List<Double> vector;
+    private List<Float> vector;
 
     private String faceDataKey;
 

+ 146 - 0
centers/AuthCenter/AuthServer/src/main/java/com/github/microservice/auth/server/core/helper/FaceHelper.java

@@ -0,0 +1,146 @@
+package com.github.microservice.auth.server.core.helper;
+
+import com.github.microservice.core.util.JsonUtil;
+import lombok.SneakyThrows;
+import org.apache.tomcat.util.digester.DocumentProperties;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StreamUtils;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.util.Pool;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+@Component
+public class FaceHelper {
+
+    @Value("${deepface.url}")
+    private String url;
+
+//    @Value("${deepface.detectorBackend}")
+//    private String detectorBackend;
+
+    @SneakyThrows
+    public HttpResponse<String> uploadFs(InputStream inputStream, String key){
+
+        // Generate a boundary for multipart/form-data
+        String boundary = UUID.randomUUID().toString();
+
+        // Build the multipart body as byte array
+        byte[] multipartBody = buildMultipartBody(boundary, Map.of(
+                "img", inputStream,
+                "key", key
+        ));
+
+        // Create the HttpClient instance
+        HttpClient client = HttpClient.newHttpClient();
+
+        // Build the HTTP request
+        HttpRequest request = HttpRequest.newBuilder()
+                .uri(URI.create(url + "/store/put"))
+                .header("Content-Type", "multipart/form-data; boundary=" + boundary)
+                .POST(HttpRequest.BodyPublishers.ofByteArray(multipartBody))
+                .build();
+
+        // Send the request and get the response
+        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+        // Print the response
+        System.out.println("Response Code: " + response.statusCode());
+        System.out.println("Response Body: " + response.body());
+        return response;
+    }
+
+    @SneakyThrows
+    public HttpResponse<String> search(InputStream inputStream, Integer maxSize){
+
+        // Generate a boundary for multipart/form-data
+        String boundary = UUID.randomUUID().toString();
+
+        // Build the multipart body as byte array
+        byte[] multipartBody = buildMultipartBody(boundary, Map.of(
+                "img", inputStream,
+                "max_size", maxSize
+        ));
+
+        // Create the HttpClient instance
+        HttpClient client = HttpClient.newHttpClient();
+
+        // Build the HTTP request
+        HttpRequest request = HttpRequest.newBuilder()
+                .uri(URI.create(url + "/store/search"))
+                .header("Content-Type", "multipart/form-data; boundary=" + boundary)
+                .POST(HttpRequest.BodyPublishers.ofByteArray(multipartBody))
+                .build();
+
+        // Send the request and get the response
+        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+
+        // Print the response
+        System.out.println("Response Code: " + response.statusCode());
+        System.out.println("Response Body: " + response.body());
+        return response;
+    }
+
+    @SneakyThrows
+    public HttpResponse<String> delete(String key){
+        // Create the HttpClient instance
+        HttpClient client = HttpClient.newHttpClient();
+        String data = JsonUtil.toJson(Map.of("key", key));
+        // Build the HTTP request
+        HttpRequest request = HttpRequest.newBuilder()
+                .uri(URI.create(url + "/store/remove"))
+                .header("Content-Type", "application/json")
+                .POST(HttpRequest.BodyPublishers.ofByteArray(data.getBytes(StandardCharsets.UTF_8)))
+                .build();
+        // Send the request and get the response
+        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
+        // Print the response
+        System.out.println("Response Code: " + response.statusCode());
+        System.out.println("Response Body: " + response.body());
+        return response;
+    }
+
+    private static byte[] buildMultipartBody(String boundary, Map<String, Object> formData) throws Exception {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+        for (Map.Entry<String, Object> entry : formData.entrySet()) {
+            String key = entry.getKey();
+            Object value = entry.getValue();
+
+            outputStream.write(("--" + boundary + "\r\n").getBytes(StandardCharsets.UTF_8));
+
+            if (value instanceof InputStream) {
+                InputStream inputStream = (InputStream) value;
+
+                outputStream.write(("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"uploaded_file\"\r\n").getBytes(StandardCharsets.UTF_8));
+                outputStream.write("Content-Type: application/octet-stream\r\n\r\n".getBytes(StandardCharsets.UTF_8));
+
+//                byte[] buffer = new byte[8192];
+//                int bytesRead;
+//                while ((bytesRead = inputStream.read(buffer)) != -1) {
+//                    outputStream.write(buffer, bytesRead);
+//                }
+                StreamUtils.copy(inputStream,outputStream);
+
+                outputStream.write("\r\n".getBytes(StandardCharsets.UTF_8));
+            } else {
+                outputStream.write(("Content-Disposition: form-data; name=\"" + key + "\"\r\n\r\n").getBytes(StandardCharsets.UTF_8));
+                outputStream.write((value.toString() + "\r\n").getBytes(StandardCharsets.UTF_8));
+            }
+        }
+
+        outputStream.write(("--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8));
+
+        return outputStream.toByteArray();
+    }
+}

+ 7 - 2
centers/AuthCenter/AuthServer/src/main/java/com/github/microservice/auth/server/core/model/ResultsModel.java → centers/AuthCenter/AuthServer/src/main/java/com/github/microservice/auth/server/core/model/FacePutResultsModel.java

@@ -9,14 +9,19 @@ import java.util.List;
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
-public class ResultsModel {
+public class FacePutResultsModel {
+
+    private String detector_backend;
+
+    private String model_name;
+
     private List<Result> results;
 
     @Data
     @AllArgsConstructor
     @NoArgsConstructor
     public static class Result {
-        private List<Double> embedding;
+        private List<Float> embedding;
         private double faceConfidence;
         private FacialArea facialArea;
 

+ 28 - 0
centers/AuthCenter/AuthServer/src/main/java/com/github/microservice/auth/server/core/model/FaceSearchResultModel.java

@@ -0,0 +1,28 @@
+package com.github.microservice.auth.server.core.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class FaceSearchResultModel {
+
+    private List<Item> items;
+
+    private Double represent;
+
+    private Double search;
+
+    @Data
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class Item {
+        private String key;
+
+        private Double score;
+    }
+}

+ 33 - 305
centers/AuthCenter/AuthServer/src/main/java/com/github/microservice/auth/server/core/service/local/UserFaceServiceImpl.java

@@ -2,8 +2,10 @@ package com.github.microservice.auth.server.core.service.local;
 
 import com.github.microservice.auth.client.content.ResultContent;
 import com.github.microservice.auth.client.content.ResultState;
-import com.github.microservice.auth.server.core.conf.RedisearchConf;
-import com.github.microservice.auth.server.core.model.RedisearchResultModel;
+import com.github.microservice.auth.security.helper.AuthHelper;
+import com.github.microservice.auth.server.core.helper.FaceHelper;
+import com.github.microservice.auth.server.core.model.FacePutResultsModel;
+import com.github.microservice.auth.server.core.model.FaceSearchResultModel;
 import com.github.microservice.auth.client.model.UserFaceQueryModel;
 import com.github.microservice.auth.client.model.UserFaceUploadModel;
 import com.github.microservice.auth.client.service.UserFaceService;
@@ -11,10 +13,7 @@ import com.github.microservice.auth.server.core.dao.UserDao;
 import com.github.microservice.auth.server.core.dao.UserFaceDao;
 import com.github.microservice.auth.server.core.domain.User;
 import com.github.microservice.auth.server.core.domain.UserFace;
-import com.github.microservice.auth.server.core.model.ResultsModel;
-import com.github.microservice.auth.server.core.util.FaceUtil;
 import com.github.microservice.core.util.JsonUtil;
-import com.github.microservice.core.util.bean.BeanUtil;
 import com.mongodb.client.gridfs.GridFSFindIterable;
 import com.mongodb.client.gridfs.model.GridFSFile;
 import jakarta.validation.constraints.NotNull;
@@ -22,17 +21,11 @@ import lombok.Cleanup;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.kafka.common.protocol.types.Field;
-import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.cglib.beans.BeanMap;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.gridfs.GridFsTemplate;
-import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
-import org.springframework.web.client.RestTemplate;
 import org.springframework.web.multipart.MultipartFile;
 import redis.clients.jedis.JedisPooled;
 
@@ -43,9 +36,7 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.math.BigDecimal;
 import java.net.http.HttpResponse;
-import java.nio.ByteBuffer;
 import java.util.*;
 import java.util.List;
 
@@ -63,18 +54,8 @@ public class UserFaceServiceImpl implements UserFaceService {
     UserFaceDao userFaceDao;
 
     @Autowired
-    RestTemplate restTemplate;
+    FaceHelper faceHelper;
 
-    @Value("${faceImage.width}")
-    private int width;
-
-    @Value("${faceImage.height}")
-    private int height;
-    @Autowired
-    private PasswordEncoder passwordEncoder;
-
-    @Autowired
-    RedisearchConf redisearchConf;
 
     /**
      * 上传人脸图片
@@ -93,133 +74,48 @@ public class UserFaceServiceImpl implements UserFaceService {
         if (user == null) {
             return ResultContent.build(ResultState.UserNotExists);
         }
+
+        String key = user.getId() + "_" + System.currentTimeMillis();
+        UserFace userFace = new UserFace();
         boolean isUpdate = StringUtils.isNotBlank(userFaceUploadModel.getUpdateFaceFileId());
         if (isUpdate) {
             //替换原人脸图片
-            if (userFaceDao.existsByUserIdAndFaceFSId(userFaceUploadModel.getUserId(), userFaceUploadModel.getUpdateFaceFileId())) {
+            userFace = userFaceDao.findTopByUserIdAndFaceFSId(userFaceUploadModel.getUserId(), userFaceUploadModel.getUpdateFaceFileId());
+            if (userFace == null) {
                 return ResultContent.build(ResultState.FaceNotExists);
             }
+            key = userFace.getFaceDataKey();
+            HttpResponse<String> delete = faceHelper.delete(key);
+            if (delete.statusCode() != 200) {
+                return ResultContent.build(ResultState.FaceDataDeleteFail);
+            }
         }
 
-        MultipartFile file = userFaceUploadModel.getFile();
-        InputStream inputStream = file.getInputStream();
-
-
-        //TODO 调整图片大小
-//        byte[] bytes = imageResizer(userFaceUploadModel.getUserFace());
-        //TODO 上传人脸库,获取向量
-        HttpResponse<String> response = FaceUtil.uploadFs("http://172.24.50.53:5001/v2/represent", inputStream, "yunet");
+        //上传人脸库,获取向量
+        String fileBase64 = userFaceUploadModel.getFile();
+        byte[] decode = Base64.getDecoder().decode(fileBase64);
+        @Cleanup InputStream inputStream = new ByteArrayInputStream(decode);
 
+        HttpResponse<String> response = faceHelper.uploadFs(inputStream, key);
         if (response.statusCode() != 200) {
             return ResultContent.build(ResultState.Fail, "人脸特征提取失败");
         }
 
-        final String body = response.body();
-
-        ResultsModel  resultsModel = JsonUtil.toObject(body, ResultsModel.class);
+        FacePutResultsModel resultsModel = JsonUtil.toObject(response.body(), FacePutResultsModel.class);
         System.out.println(resultsModel);
 
 
-
-        System.out.println(body);
-        List<Double> embedding = resultsModel.getResults().get(0).getEmbedding();
-
-
-
-        JedisPooled jedis = new JedisPooled(redisearchConf.getHost(), redisearchConf.getPort(), null, redisearchConf.getPassword());
-
-        // 动态设置变量
-        String indexName = redisearchConf.getIndexName();
-        String prefix = redisearchConf.getPrefix();
-        String fieldName = redisearchConf.getFieldName();
-        String dim = redisearchConf.getDim();
-        initIndex(jedis, indexName, prefix, fieldName, dim);
-
-
-        // 将 List<Float> 转换为 float[]
-        double[] vector = listToFloatArray(embedding);
-        // 归一化向量
-        double norm = calculateNorm(vector);
-        for (int i = 0; i < vector.length; i++) {
-            vector[i] /= norm;
-        }
-
-        // 转换为字节数组 (FLOAT32,每个值占 4 字节)
-        ByteBuffer buffer = ByteBuffer.allocate(vector.length * Double.BYTES);
-        for (double value : vector) {
-            buffer.putDouble(value);
-        }
-
-        String key = prefix + user.getId() + System.currentTimeMillis();
-
-        long hset = jedis.hset(key.getBytes(), fieldName.getBytes(), buffer.array());
-
-        System.out.println(hset);
-        jedis.close();
         //图片入库
         String fileId = gridFsTemplate.store(inputStream, UUID.randomUUID().toString(), Map.of("userId", userFaceUploadModel.getUserId())).toHexString();
-        UserFace userFace = new UserFace();
-        if (isUpdate) {
-            userFace = userFaceDao.findTopByUserIdAndFaceFSId(userFaceUploadModel.getUserId(), userFaceUploadModel.getUpdateFaceFileId());
-        }
+
         //向量入库
         userFace.setUserId(user.getId());
         userFace.setFaceFSId(fileId);
-        //设置向量
-        userFace.setVector(embedding);
         userFace.setFaceDataKey(key);
         userFaceDao.save(userFace);
         return ResultContent.buildContent(fileId);
     }
 
-    private static void initIndex(JedisPooled jedis, String indexName, String prefix, String fieldName, String dim) {
-        try {
-            Map<String, Object> stringObjectMap = jedis.ftInfo(indexName);
-            //TODO 检查参数是否正确
-        }catch (Exception e) {
-            //创建索引
-            // Lua 脚本模板
-            String createIndexScript = "local indexName = KEYS[1] " +
-                    "local prefix = KEYS[2] " +
-                    "local fieldName = KEYS[3] " +
-                    "local dim = ARGV[1] " +
-                    "redis.call('FT.CREATE', indexName, 'ON', 'HASH', 'PREFIX', '1', prefix, " +
-                    "'SCHEMA', fieldName, 'VECTOR', 'FLAT', '6', " +
-                    "'TYPE', 'FLOAT64', 'DIM', dim, 'DISTANCE_METRIC', 'COSINE')";
-
-
-            String a = """
-                    
-                   """;
-
-                // 执行 Lua 脚本并传递变量
-                Object result = jedis.eval(createIndexScript,
-                        // KEYS 参数:索引名、前缀、字段名
-                        List.of(indexName, prefix, fieldName), List.of(dim));
-                System.out.println("Index created successfully: " + result);
-        }
-    }
-
-
-    private static double calculateNorm(double[] vector) {
-        double sum = 0;
-        for (double v : vector) {
-            sum += v * v;  // 对每个分量平方求和
-        }
-        return (double) Math.sqrt(sum);  // 求平方和的平方根
-    }
-
-    public static double[] listToFloatArray(List<Double> list) {
-        // 创建一个与 List 大小相同的 float 数组
-        double[] array = new double[list.size()];
-
-        // 使用普通的 for 循环将 List 中的每个元素放入数组中
-        for (int i = 0; i < list.size(); i++) {
-            array[i] = list.get(i);  // 自动拆箱,将 Float 转换为 float
-        }
-
-        return array;
-    }
 
     /**
      * 删除用户指定人脸图片
@@ -240,12 +136,11 @@ public class UserFaceServiceImpl implements UserFaceService {
         }
 
         //删除人脸库的人脸
-        JedisPooled jedis = new JedisPooled(redisearchConf.getHost(), redisearchConf.getPort(), null, redisearchConf.getPassword());
+        HttpResponse<String> response = faceHelper.delete(userFace.getFaceDataKey());
 
-        long del = jedis.del(userFace.getFaceDataKey());
-        if (del == 0) {
+        if (response.statusCode() != 200) {
             log.error("人脸库删除数据失败:key:{}", userFace.getFaceDataKey());
-            return ResultContent.build(ResultState.Fail);
+            return ResultContent.build(ResultState.Fail, "人脸库数据删除失败");
         }
         userFaceDao.deleteById(userFace.getId());
         return ResultContent.build(ResultState.Success);
@@ -289,133 +184,18 @@ public class UserFaceServiceImpl implements UserFaceService {
      */
     @SneakyThrows
     public ResultContent<Object> matches(MultipartFile file) {
-        //TODO 上传人脸服务,获取向量
-        InputStream inputStream = file.getInputStream();
-
-        //TODO 调整图片大小
-//        byte[] bytes = imageResizer(userFaceUploadModel.getUserFace());
-        //TODO 上传人脸库,获取向量
-        HttpResponse<String> response = FaceUtil.uploadFs("http://172.24.50.53:5001/v2/represent", inputStream, "yunet");
+        @Cleanup InputStream inputStream = file.getInputStream();
+        HttpResponse<String> response = faceHelper.search(inputStream, 1);
 
         if (response.statusCode() != 200) {
             return ResultContent.build(ResultState.Fail, "人脸特征提取失败");
         }
-
-        final String body = response.body();
-
-        ResultsModel  resultsModel = JsonUtil.toObject(body, ResultsModel.class);
-        System.out.println(resultsModel);
-
-
-
-        System.out.println(body);
-        List<Double> embedding = resultsModel.getResults().get(0).getEmbedding();
-        double[] vector = listToFloatArray(embedding);
-
-        double norm = calculateNorm(vector);
-        for (int i = 0; i < vector.length; i++) {
-            vector[i] /= norm;
-        }
-
-        // 转换为字节数组 (FLOAT32,每个值占 4 字节)
-        ByteBuffer buffer = ByteBuffer.allocate(vector.length * Double.BYTES);
-        for (double value : vector) {
-            buffer.putDouble(value);
-        }
-        // 打印字节数组的内容
-        byte[] queryVector = buffer.array();
-        System.out.println("queryVector.length");
-        System.out.println(queryVector.length);
-
-        // 定义查询参数
-        String indexName = redisearchConf.getIndexName();
-        String prefix = redisearchConf.getPrefix();
-        String fieldName = redisearchConf.getFieldName();
-        String dim = redisearchConf.getDim();
-//        String queryVectorString = "\x8d5\xf7=\x91w:=*\x19h=\x05u\xc8=\xfa\x06\xb0=+\x00\xa8=\x96\x9b\x02>\xd5\xb4\x19>\xf1Pf=\xb5U\xf7=\xbc\xaf\x10>\xd9\x96\x18>\xa5hk=\xcd\xd3\x83=\x86\xcd\x18>\xbd\x80\x0b>A$\xf1=\xb2\xcf\xc5=K\xd4\xe2=\x83c\x01>\x02\x85\xbb=c$'=\x00-\x14>\x0c[i=\x80\x0b\xaf=\x15r\x0c>\xb2\x1f\xbf<\xe78\x8e=\x9b\xb9\xdf=\xce\xd9\xa3=\xe7\xaa\r>\xee\\\xaa=\x83\x95g=g/R=x\xc0\x0b>\xfd\xe7\x07>M\xb8\x0f=\x05\xbcA=q\x0e\xbd=R\x98\x8c;b\xfe\x85=\xea%+=\xe7\x98\x81:0H\x07>\xd9g\x04>\xe5\xe3\x8e=oM]=%\xbe\x92<\x1e\xf7\x05>\x11\xa9\x1a>\xce1\xe9<Z\x8d\x0e>\xe0<\x07>\xcar\x86=\xde\x0e\x9e=l\x8e\xf6=\xa9\x04\xa7=7\xe9G=\n[\x05<\xcd\xb3\x1c=\xf26\xe2=\xfd\xbd\xa4=\x94\x95\xea=\xb3\x9f\xa7<*Z\xfb;o\xbb\xb7<\xae\xea\xdb=\x03\x82\xcb<\xc8\x12\x1a<\xfa\xf9\x91<\xacJ\x18=\x18\xcb\xb0=n\x87\x92=6j\xf0=\xa7\xf0\x92<]!\xb4=\xf8\x01\x85=\xaf\xfa\xd7=\xbbE\xe5;f\x9d\xeb<^\xe1\x00>\xa3\xb9\x93=\x83\xcb\x14>\xdb\x86\x1a>\xe1\xa0\xd7=Cu*=9\xfd\x8f=e\xe2\xe7<y1.=2T\x91=59\x92=\xd8\xb5\xbd=\xd5\xf1\xa1=\xf4\xd6\x08>m\xd3K<\x1b\xc5M=\x93#\xb2=\xc0\x8b\xb4=\xa0Ph=\xe4s\x92=\xd7\x93\xb4=\xa3x\\;Q\x84\x08>\x8b&\xdc<a\x0e\x1d=\x83\x9c\x9e<\x9a\x84\xac<\xceS\xdf=\x8bT\xab;\xa7\xfd\r=\x88j\x10<\xcd\x07\xee<\x07\x8e\x81<t^\xdc=\xcf\xcf\xbd=\xeb\x92\xa1<[\x88\xd7=\xf0\xbb\x00>\x8e*\x18>\x82\xad\x11>yG\xbb<g\x07\x06>\xa08\x8d<\x01C\x1b>\xc7\xfd:=\x0b\x93\x10;\xbce>=\xb9\xdba=";
-        String topK = "1";  // 返回Top 10个结果
-//        String vectorField = "doc_embedding";  // 向量字段名称
-
-        String queryScript  = "local indexName = KEYS[1] "
-                + "local queryVector = ARGV[1] "
-                + "local topK = tonumber(ARGV[2]) "
-                + "local vectorField = ARGV[3] "
-                + "local query = '*=>[KNN ' .. topK .. ' @' .. vectorField .. ' $BLOB AS score]' "
-                + "local result = redis.call('FT.SEARCH', indexName, query, "
-                + "'PARAMS', '2', 'BLOB', queryVector, "
-                + "'RETURN', '1', 'score', "
-                + "'SORTBY', 'score', "
-                + "'DIALECT', '2') "
-                + "return result";
-        // 执行Redis查询
-
-
-        JedisPooled jedis = new JedisPooled( redisearchConf.getHost(), redisearchConf.getPort(), null, redisearchConf.getPassword());
-
-        // 动态设置变量
-
-        initIndex(jedis, indexName, prefix, fieldName, dim);
-
-        Object result = jedis.eval(queryScript.getBytes(), 1, indexName.getBytes(), queryVector, topK.getBytes(), fieldName.getBytes());
-        // 关闭连接
-        jedis.close();
-        // 打印结果
-        System.out.println("Query Result: " + JsonUtil.toJson(result));
-        RedisearchResultModel redisearchResultModel = processQueryResult(result);
-        //TODO 解析结果,返回userId
-        if (redisearchResultModel.getCount() == 0L) {
-            return ResultContent.build(ResultState.FaceNoMatches);
-        }
-
-        if (redisearchResultModel.getScoreValue().equals("nan") || redisearchResultModel.getScoreValue().equals("-nan")) {
-            return ResultContent.build(ResultState.FaceNoMatches);
-        }
-
-        BigDecimal scoreValue = new BigDecimal(redisearchResultModel.getScoreValue());
-        if (scoreValue.compareTo(BigDecimal.ZERO) == 0 || scoreValue.compareTo(new BigDecimal(redisearchConf.getScore())) > 0) {
-            String dataKey = redisearchResultModel.getDataKey();
-            dataKey = dataKey.replace(prefix, "");
-            String userId = dataKey.substring(0, 24);
-            redisearchResultModel.setUserId(userId);
-            return ResultContent.buildContent(BeanUtil.bean2Map(redisearchResultModel));
-        }else {
-            return ResultContent.build(ResultState.FaceNoMatches);
-        }
-
-    }
-
-
-    private static RedisearchResultModel processQueryResult(Object result) {
-        RedisearchResultModel redisearchResultModel = new RedisearchResultModel();
-
-        if (result instanceof List){
-            List<Object> list = (List) result;
-            for (Object item : list) {
-                if (item instanceof byte[]) {// data key
-                    byte[] bytes = ((byte[]) item);
-                    System.out.println("Found vector: " + new String(bytes)); // 可以根据需要进一步处理
-                    redisearchResultModel.setDataKey(new String(bytes));
-                } else if (item instanceof Long){// 返回的结果数
-                    System.out.println("Found Long: " + item);
-                    redisearchResultModel.setCount((Long)item);
-                }else if (item instanceof List) {// 对应的 距离
-                    List<Object> itemList = (List) item;
-                    for (Object item1 : itemList) {
-                        if (item1 instanceof byte[]){
-                            byte[] bytes = ((byte[]) item1);
-                            System.out.println("Found vector: " + new String(bytes)); // 可以根据需要进一步处理
-                            String str = new String(bytes);
-                            if (str.equals("score")){
-                                redisearchResultModel.setScoreKey(str);
-                            }else {
-                                redisearchResultModel.setScoreValue(str);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return redisearchResultModel;
+        FaceSearchResultModel  resultsModel = JsonUtil.toObject(response.body(), FaceSearchResultModel.class);
+        List<FaceSearchResultModel.Item> items = resultsModel.getItems().stream().map(item -> {
+            item.setKey(item.getKey().substring(0, item.getKey().lastIndexOf("_")));
+            return item;
+        }).toList();
+        return ResultContent.buildContent(Map.of("items", items));
     }
 
     /**
@@ -437,56 +217,4 @@ public class UserFaceServiceImpl implements UserFaceService {
         return Base64.getEncoder().encodeToString(bytes);
     }
 
-
-    private byte[] imageResizer(String base64Image) {
-        // 解码 Base64 字符串为 BufferedImage
-        BufferedImage originalImage = decodeBase64ToImage(base64Image);
-
-        // 调整图像大小
-        BufferedImage resizedImage = resizeImage(originalImage, width, height);
-
-        // 将调整后的图像转换为字节组
-        return encodeImageToBytes(resizedImage);
-
-    }
-
-//    public static void resizeImage(InputStream inputStream, int width, int height) throws IOException {
-//        BufferedImage inputImage = ImageIO.read(inputStream);
-//
-//        // 创建一个输出的 BufferedImage
-//        BufferedImage outputImage = new BufferedImage(width, height, inputImage.getType());
-//
-//        // 使用 Graphics2D 绘制缩放后的图片
-//        Graphics2D g2d = outputImage.createGraphics();
-//        g2d.drawImage(inputImage, 0, 0, width, height, null);
-//        g2d.dispose();
-//
-//        // 输出到文件
-//        String formatName = outputFile.getName().substring(outputFile.getName().lastIndexOf(".") + 1);
-//        ImageIO.write(outputImage, formatName, outputFile);
-//    }
-
-    private static BufferedImage resizeImage(BufferedImage originalImage, int width, int height) {
-        BufferedImage resizedImage = new BufferedImage(width, height, originalImage.getType());
-        Graphics2D g = resizedImage.createGraphics();
-        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-        g.drawImage(originalImage, 0, 0, width, height, null);
-        g.dispose();
-
-        return resizedImage;
-    }
-
-    @SneakyThrows
-    private static BufferedImage decodeBase64ToImage(String base64Image) {
-        byte[] imageBytes = Base64.getDecoder().decode(base64Image);
-        @Cleanup ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes);
-        return ImageIO.read(bais);
-    }
-
-    @SneakyThrows
-    private static byte[] encodeImageToBytes(BufferedImage image) {
-        @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        ImageIO.write(image, "png", baos);
-        return baos.toByteArray();
-    }
 }

+ 2 - 13
centers/AuthCenter/AuthServer/src/main/resources/application-dev.yml

@@ -48,19 +48,8 @@ init:
               passWord: xiaoxue
 
 
-faceImage:
-  width: 1280
-  height: 720
-
-redisearch:
-  host: 172.24.50.53
-  port: 6379
-  password: 8756redis2024
-  index-name: face_index
-  field-name: doc_embedding
-  dim: 4096
-  score: 0.85
-  prefix: 'docs:'
+deepface:
+  url: http://127.0.0.1:5000
 
 springdoc:
   api-docs: