diff --git a/pom.xml b/pom.xml
index 0e9142ba08ee9cd29daf6b09d5be7b00604092da..89325f88123371d575b1a16d76c0bd7cab3a01ab 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,6 +20,7 @@
0.27.0
+
org.springframework.boot
spring-boot-starter
@@ -222,6 +223,12 @@
5.2.0-beta5
+
+ io.github.javpower
+ vectorex-starter
+ 1.5.2
+
+
diff --git a/src/main/java/com/github/javpower/javavision/controller/ImageController.java b/src/main/java/com/github/javpower/javavision/controller/ImageController.java
index 13f976a2b226b9b3ec1d9f5b62d2c296e03c7e86..5d02a76cda94df71d16e2574a387323c807e4800 100644
--- a/src/main/java/com/github/javpower/javavision/controller/ImageController.java
+++ b/src/main/java/com/github/javpower/javavision/controller/ImageController.java
@@ -5,6 +5,7 @@ import com.github.javpower.javavision.es.response.SearchResult;
import com.github.javpower.javavision.es.service.ImageSearchService;
import com.github.javpower.javavision.milvus.service.ImageMilvusService;
import com.github.javpower.javavision.service.IImageService;
+import com.github.javpower.javavision.vectorex.service.ImageVectoRexService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
@@ -31,10 +32,10 @@ public class ImageController implements EnvironmentAware {
private IImageService imageService;
private String impl;
- public ImageController(Environment environment,ImageSearchService imageSearchService,ImageMilvusService imageMilvusService) {
+ public ImageController(Environment environment,ImageSearchService imageSearchService,ImageMilvusService imageMilvusService,ImageVectoRexService imageVectoRexService) {
// 从环境对象中获取配置项的值
this.impl = environment.getProperty("image.service");
- initializeService(imageSearchService, imageMilvusService);
+ initializeService(imageSearchService, imageMilvusService,imageVectoRexService);
}
@PostMapping("/add")
@Operation(summary = "添加")
@@ -46,15 +47,22 @@ public class ImageController implements EnvironmentAware {
public List search(Integer k, MultipartFile file, HttpServletRequest request) throws Exception {
return imageService.search(file.getInputStream(),k);
}
+ @PostMapping("/del")
+ @Operation(summary = "删除")
+ public void del(String imageId, HttpServletRequest request) throws Exception {
+ imageService.del(imageId);
+ }
@Override
public void setEnvironment(Environment environment) {
}
// 根据配置项初始化服务
- private void initializeService(ImageSearchService imageSearchService, ImageMilvusService imageMilvusService) {
+ private void initializeService(ImageSearchService imageSearchService, ImageMilvusService imageMilvusService, ImageVectoRexService imageVectoRexService) {
if ("milvus".equalsIgnoreCase(impl)) {
this.imageService = imageMilvusService;
+ } if ("vectorex".equalsIgnoreCase(impl)) {
+ this.imageService = imageVectoRexService;
} else {
this.imageService = imageSearchService;
}
diff --git a/src/main/java/com/github/javpower/javavision/es/service/ImageSearchService.java b/src/main/java/com/github/javpower/javavision/es/service/ImageSearchService.java
index 7962028624d3d6c763c0ae650d7066714caec093..07c67cd9a5916ecaba0b71bde9224be80ee225ef 100644
--- a/src/main/java/com/github/javpower/javavision/es/service/ImageSearchService.java
+++ b/src/main/java/com/github/javpower/javavision/es/service/ImageSearchService.java
@@ -86,6 +86,12 @@ public class ImageSearchService implements IImageService {
}
return list;
}
+
+ @Override
+ public void del(String imageId) {
+
+ }
+
private void batchAdd(List imageSearchList) throws IOException, ModelException, TranslateException, OrtException {
//批量上传请求
BulkRequest bulkRequest = new BulkRequest(EsConfig.IMAGE_SEARCH_INDEX);
diff --git a/src/main/java/com/github/javpower/javavision/milvus/service/ImageMilvusService.java b/src/main/java/com/github/javpower/javavision/milvus/service/ImageMilvusService.java
index cad8b63c5fc3b6d2071569828ab6773e33f7f1e9..612fa0a9d0300a898866171859e3332827eaf816 100644
--- a/src/main/java/com/github/javpower/javavision/milvus/service/ImageMilvusService.java
+++ b/src/main/java/com/github/javpower/javavision/milvus/service/ImageMilvusService.java
@@ -67,6 +67,12 @@ public class ImageMilvusService implements IImageService {
}
return res;
}
+
+ @Override
+ public void del(String imageId) {
+ imageMilvusMapper.deleteWrapper().eq(Image::getImageId,imageId).remove();
+ }
+
private void batchAdd(List imageSearchList) throws IOException, ModelException, TranslateException, OrtException {
//批量上传请求
for (Image imageSearch : imageSearchList) {
diff --git a/src/main/java/com/github/javpower/javavision/service/IImageService.java b/src/main/java/com/github/javpower/javavision/service/IImageService.java
index c74729901f6a01e9ae3760fee5d1013ba0a8bdc9..0ccb72c9f27cd4a728ab21f96a91bf9d2f3e6328 100644
--- a/src/main/java/com/github/javpower/javavision/service/IImageService.java
+++ b/src/main/java/com/github/javpower/javavision/service/IImageService.java
@@ -15,4 +15,5 @@ public interface IImageService {
List search(InputStream input, int k) throws IOException, ModelException, TranslateException, OrtException;
+ void del(String imageId);
}
diff --git a/src/main/java/com/github/javpower/javavision/util/ImageFeatureUtil.java b/src/main/java/com/github/javpower/javavision/util/ImageFeatureUtil.java
index 2268abaaa4ed42926d4e5a65d36a176c14032ad4..84537e5aa0f0e847f13febb8229d31b359fb239c 100644
--- a/src/main/java/com/github/javpower/javavision/util/ImageFeatureUtil.java
+++ b/src/main/java/com/github/javpower/javavision/util/ImageFeatureUtil.java
@@ -6,7 +6,6 @@ import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.ImageFactory;
import ai.djl.repository.zoo.Criteria;
import ai.djl.repository.zoo.ModelZoo;
-import ai.djl.repository.zoo.ZooModel;
import ai.djl.translate.TranslateException;
import ai.onnxruntime.OrtException;
import com.github.javpower.javavision.detect.ImageFeatureDetect;
@@ -22,22 +21,62 @@ import java.nio.file.Paths;
*/
public class ImageFeatureUtil {
private static final Logger logger = LoggerFactory.getLogger(ImageFeatureUtil.class);
+ private static volatile ImageFeatureUtil instance;
+ private Predictor predictor;
- private ImageFeatureUtil() {}
+ // 单例的私有构造函数
+ private ImageFeatureUtil() {
+ try {
+ ImageFeatureDetect imageFeatureDetect = new ImageFeatureDetect("image_feature.zip");
+ Criteria criteria = imageFeatureDetect.criteria();
+ this.predictor = ModelZoo.loadModel(criteria).newPredictor();
+ } catch (IOException | ModelException | OrtException e) {
+ logger.error("Failed to initialize Predictor", e);
+ }
+ }
+
+ // 获取单例实例的方法
+ public static ImageFeatureUtil getInstance() {
+ if (instance == null) {
+ synchronized (ImageFeatureUtil.class) {
+ if (instance == null) {
+ instance = new ImageFeatureUtil();
+ }
+ }
+ }
+ return instance;
+ }
- //获取图片特征
+ // 静态的runOcr方法,用于外部调用
public static float[] runOcr(String path) throws IOException, ModelException, TranslateException, OrtException {
Path imageFile = Paths.get(path);
Image image = ImageFactory.getInstance().fromFile(imageFile);
return runOcr(image);
}
- public static float[] runOcr(Image image) throws IOException, ModelException, TranslateException, OrtException {
- ImageFeatureDetect imageFeatureDetect = new ImageFeatureDetect("image_feature.zip");
- Criteria criteria = imageFeatureDetect.criteria();
- try (ZooModel model = ModelZoo.loadModel(criteria);
- Predictor predictor = model.newPredictor()) {
- float[] predict = predictor.predict(image);
- return predict;
+
+ public static float[] runOcr(Image image) throws ModelException, TranslateException, OrtException {
+ // 通过getInstance()获取单例实例,然后调用其predict方法
+ return getInstance().predict(image);
+ }
+
+ // 非静态的predict方法,仅供单例内部使用
+ private float[] predict(Image image) throws ModelException, TranslateException, OrtException {
+ return predictor.predict(image);
+ }
+
+ // 关闭Predictor的方法
+ public void close() {
+ if (predictor != null) {
+ try {
+ predictor.close();
+ } catch (Exception e) {
+ logger.error("Failed to close Predictor", e);
+ }
}
}
+
+ // 添加关闭钩子
+ static {
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> getInstance().close()));
+ }
}
diff --git a/src/main/java/com/github/javpower/javavision/vectorex/mapper/FaceVectoRexMapper.java b/src/main/java/com/github/javpower/javavision/vectorex/mapper/FaceVectoRexMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..035feb574afaab364bcfd8eba76faf9a3e49ce4e
--- /dev/null
+++ b/src/main/java/com/github/javpower/javavision/vectorex/mapper/FaceVectoRexMapper.java
@@ -0,0 +1,10 @@
+package com.github.javpower.javavision.vectorex.mapper;
+
+import com.github.javpower.javavision.vectorex.model.Face;
+import io.github.javpower.vectorexbootstater.mapper.VectorRexMapper;
+import org.springframework.stereotype.Component;
+
+@Component
+public class FaceVectoRexMapper extends VectorRexMapper {
+
+}
diff --git a/src/main/java/com/github/javpower/javavision/vectorex/mapper/ImageVectoRexMapper.java b/src/main/java/com/github/javpower/javavision/vectorex/mapper/ImageVectoRexMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..757eeb67f9968221b65f1cd62f110960f1a5bb4b
--- /dev/null
+++ b/src/main/java/com/github/javpower/javavision/vectorex/mapper/ImageVectoRexMapper.java
@@ -0,0 +1,10 @@
+package com.github.javpower.javavision.vectorex.mapper;
+
+import com.github.javpower.javavision.vectorex.model.Image;
+import io.github.javpower.vectorexbootstater.mapper.VectorRexMapper;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ImageVectoRexMapper extends VectorRexMapper {
+
+}
diff --git a/src/main/java/com/github/javpower/javavision/vectorex/model/Face.java b/src/main/java/com/github/javpower/javavision/vectorex/model/Face.java
new file mode 100644
index 0000000000000000000000000000000000000000..066f2c9726c5c74db9eae3403dad623cf9da492a
--- /dev/null
+++ b/src/main/java/com/github/javpower/javavision/vectorex/model/Face.java
@@ -0,0 +1,28 @@
+package com.github.javpower.javavision.vectorex.model;
+
+import io.github.javpower.vectorex.keynote.model.MetricType;
+import io.github.javpower.vectorexcore.annotation.VectoRexCollection;
+import io.github.javpower.vectorexcore.annotation.VectoRexField;
+import io.github.javpower.vectorexcore.entity.DataType;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@VectoRexCollection(name = "face_collection")
+public class Face {
+
+ @VectoRexField(name = "person_id",isPrimaryKey = true)
+ private String personId; // 人员的唯一标识符
+
+ @VectoRexField(name = "image_url")
+ private String url;
+
+
+ @VectoRexField(name = "person_name")
+ private String personName; // 人员姓名
+
+
+ @VectoRexField(name = "face_vector", dataType = DataType.FloatVector, dimension = 256,metricType = MetricType.FLOAT_COSINE_DISTANCE)
+ private List faceVector; // 存储人脸特征的向量
+}
\ No newline at end of file
diff --git a/src/main/java/com/github/javpower/javavision/vectorex/model/Image.java b/src/main/java/com/github/javpower/javavision/vectorex/model/Image.java
new file mode 100644
index 0000000000000000000000000000000000000000..08a186ccd9c5323b492cdfe96a25f53a7e61295f
--- /dev/null
+++ b/src/main/java/com/github/javpower/javavision/vectorex/model/Image.java
@@ -0,0 +1,25 @@
+package com.github.javpower.javavision.vectorex.model;
+
+import io.github.javpower.vectorex.keynote.model.MetricType;
+import io.github.javpower.vectorexcore.annotation.VectoRexCollection;
+import io.github.javpower.vectorexcore.annotation.VectoRexField;
+import io.github.javpower.vectorexcore.entity.DataType;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@VectoRexCollection(name = "image_collection")
+public class Image {
+
+ @VectoRexField(name = "image_id",isPrimaryKey = true)
+ private String imageId; // 人员的唯一标识符
+
+
+ @VectoRexField(name = "image_url")
+ private String url;
+
+
+ @VectoRexField(name = "image_vector", dataType = DataType.FloatVector, dimension = 1024,metricType = MetricType.FLOAT_COSINE_DISTANCE)
+ private List imageVector; // 存储人脸特征的向量
+}
\ No newline at end of file
diff --git a/src/main/java/com/github/javpower/javavision/vectorex/service/FaceVectoRexService.java b/src/main/java/com/github/javpower/javavision/vectorex/service/FaceVectoRexService.java
new file mode 100644
index 0000000000000000000000000000000000000000..db079e9f8b2a025f95867095e4ec5e15e50f2e67
--- /dev/null
+++ b/src/main/java/com/github/javpower/javavision/vectorex/service/FaceVectoRexService.java
@@ -0,0 +1,70 @@
+package com.github.javpower.javavision.vectorex.service;
+
+import ai.djl.ModelException;
+import ai.djl.translate.TranslateException;
+import ai.onnxruntime.OrtException;
+import com.github.javpower.javavision.arcsoft.FaceEngineService;
+import com.github.javpower.javavision.arcsoft.entity.FaceDetectObject;
+import com.github.javpower.javavision.entity.FaceParam;
+import com.github.javpower.javavision.util.FileUtil;
+import com.github.javpower.javavision.vectorex.mapper.FaceVectoRexMapper;
+import com.github.javpower.javavision.vectorex.model.Face;
+import io.github.javpower.vectorexbootstater.core.VectoRexResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.List;
+
+@Service
+@Slf4j
+public class FaceVectoRexService {
+ @Autowired
+ private FaceVectoRexMapper faceVectoRexMapper;
+ @Autowired
+ private FileUtil fileUtil;
+ @Autowired
+ private FaceEngineService faceEngineService;
+
+ public void add(String faceId,String personName,MultipartFile file) throws IOException, ModelException, TranslateException, OrtException {
+ Face face=new Face();
+ String path = fileUtil.getPath(file);
+ List faceDetect = faceEngineService.getFaceDetect(path);
+ face.setPersonId(faceId);
+ face.setUrl(path);
+ face.setPersonName(personName);
+ face.setFaceVector(faceDetect.get(0).getFaceFeature());
+ faceVectoRexMapper.insert(face);
+
+ }
+ //图片及展示k个结果
+ public Face search(MultipartFile file) throws IOException{
+ List faceDetect = faceEngineService.getFaceDetect(file);
+ List> data = faceVectoRexMapper.queryWrapper()
+ .vector(Face::getFaceVector, faceDetect.get(0).getFaceFeature())
+ .topK(1).query();
+ for (VectoRexResult datum : data) {
+ Face entity = datum.getEntity();
+ return entity;
+ }
+ return null;
+ }
+
+ public void update(FaceParam param, MultipartFile file, HttpServletRequest request) throws IOException {
+ String path = fileUtil.getPath(file);
+ List faceDetect = faceEngineService.getFaceDetect(path);
+ Face face=new Face();
+ face.setPersonId(param.getPersonId());
+ face.setPersonName(param.getPersonName());
+ face.setFaceVector(faceDetect.get(0).getFaceFeature());
+ face.setUrl(path);
+ faceVectoRexMapper.updateById(face);
+ }
+
+ public void del(FaceParam param ,HttpServletRequest request) {
+ faceVectoRexMapper.removeById(param.getPersonId());
+ }
+}
diff --git a/src/main/java/com/github/javpower/javavision/vectorex/service/ImageVectoRexService.java b/src/main/java/com/github/javpower/javavision/vectorex/service/ImageVectoRexService.java
new file mode 100644
index 0000000000000000000000000000000000000000..508f4a4670260bad1813f2ec9c037fc29df4c1a7
--- /dev/null
+++ b/src/main/java/com/github/javpower/javavision/vectorex/service/ImageVectoRexService.java
@@ -0,0 +1,88 @@
+package com.github.javpower.javavision.vectorex.service;
+
+import ai.djl.ModelException;
+import ai.djl.modality.cv.ImageFactory;
+import ai.djl.translate.TranslateException;
+import ai.onnxruntime.OrtException;
+import com.github.javpower.javavision.es.response.SearchResult;
+import com.github.javpower.javavision.service.IImageService;
+import com.github.javpower.javavision.util.FileUtil;
+import com.github.javpower.javavision.util.ImageFeatureUtil;
+import com.github.javpower.javavision.util.ImageUtil;
+import com.github.javpower.javavision.vectorex.mapper.ImageVectoRexMapper;
+import com.github.javpower.javavision.vectorex.model.Image;
+import io.github.javpower.vectorexbootstater.core.VectoRexResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service("ImageVectoRexService")
+@Slf4j
+public class ImageVectoRexService implements IImageService {
+ @Autowired
+ private ImageVectoRexMapper imageVectoRexMapper;
+ @Autowired
+ private FileUtil fileUtil;
+
+ public void add(String imageId,MultipartFile file) throws IOException, ModelException, TranslateException, OrtException {
+ List imageSearchList=new ArrayList<>();
+ Image search=new Image();
+ String path = fileUtil.getPath(file);
+ search.setImageId(imageId);
+ search.setUrl(path);
+ imageSearchList.add(search);
+ batchAdd(imageSearchList);
+ }
+ //图片及展示k个结果
+ public List search(InputStream input,int k) throws IOException, ModelException, TranslateException, OrtException {
+ List res=new ArrayList<>();
+ try (InputStream inputStream = input) {
+ ai.djl.modality.cv.Image image = ImageFactory.getInstance().fromInputStream(inputStream);
+ float[] vector = ImageFeatureUtil.runOcr(image);
+ List floatList = new ArrayList<>();
+ // 遍历数组并将每个元素添加到列表中
+ for (float f : vector) {
+ floatList.add(f);
+ }
+ List floats = ImageUtil.normalizeVector(floatList);
+ List> data = imageVectoRexMapper.queryWrapper()
+ .vector(Image::getImageVector, floats)
+ .topK(k).query();
+ for (VectoRexResult datum : data) {
+ Image entity = datum.getEntity();
+ String url = entity.getUrl();
+ Float distance = datum.getScore();
+ Object imageId = datum.getEntity().getImageId();
+ SearchResult result=new SearchResult(url,imageId.toString(),distance);
+ res.add(result);
+ }
+ }
+ return res;
+ }
+
+ @Override
+ public void del(String imageId) {
+ imageVectoRexMapper.removeById(imageId);
+ }
+
+ private void batchAdd(List imageSearchList) throws IOException, ModelException, TranslateException, OrtException {
+ //批量上传请求
+ for (Image imageSearch : imageSearchList) {
+ float[] vector = ImageFeatureUtil.runOcr(imageSearch.getUrl());
+ List floatList = new ArrayList<>();
+ // 遍历数组并将每个元素添加到列表中
+ for (float f : vector) {
+ floatList.add(f);
+ }
+ List floats = ImageUtil.normalizeVector(floatList);
+ imageSearch.setImageVector(floats);
+ }
+ imageVectoRexMapper.insert(imageSearchList);
+ }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 1443372604ce8763150d44f2721db5499e4c8211..5a5f42f5186eabca601dce909855b0ea5498b8c1 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -24,7 +24,10 @@ spring:
multipart:
max-file-size: 20MB
max-request-size: 200MB
-
+ redis:
+ host: 127.0.0.1
+ port: 6379
+ password:
ocr:
# 文件上传相关配置
@@ -32,7 +35,7 @@ ocr:
# 上传文件访问路径
accessPath: /file/**
# 上传文件保存路径
- uploadPath: /opt/ocr/
+ uploadPath: image/
vision:
# swagger配置
openapi:
@@ -57,14 +60,19 @@ es:
milvus:
uri: https://in03-a5357975ab80da7.api.gcp-us-west1.zillizcloud.com
token: 6fab5641a3156d2666feba14390e4ef4b6d376b5dce91faed303eec91a4bdb82239b70b29eb252b981daa3170516245818d4ee12
- enable: true # 是否启用
+ enable: false # 是否启用
packages:
- com.github.javpower.javavision.milvus.model
open-log: true
- log-level: WARN
+# log-level: WARN
+
+vectorex:
+ enable: true # 是否启用
+ packages:
+ - com.github.javpower.javavision.vectorex.model
image:
- service: milvus # 或者 es
+ service: vectorex # 或者 es、milvus
# 使用虹软人脸识别,需要申请sdk密钥(该项目示例搭配的的是milvus数据库)
config:
@@ -74,4 +82,22 @@ config:
sdk-key: "xxxxxxxx"
active_key: "#此处不要填写"
detect-pool-size: 5
- compare-pool-size: 5
\ No newline at end of file
+ compare-pool-size: 5
+
+
+
+#redisson:
+# timeout: 3000 # 超时时间(毫秒)
+# address: "154.201.90.228:6379" # Redis地址
+# password: "" # Redis密码
+
+jedis:
+ nodes:
+ - 127.0.0.1:6379
+ pool:
+ minIdle: 64
+ maxIdle: 64
+ maxTotal: 128
+ timeout: 500
+
+