diff --git a/README.md b/README.md
index 41dbf14..2491c4b 100644
--- a/README.md
+++ b/README.md
@@ -111,7 +111,6 @@
| smartjavaai-common | 基础通用模块,封装了公共功能,供各算法模块共享使用 |
| smartjavaai-face | 人脸功能模块 |
| smartjavaai-objectdetection | 目标检测模块 |
-| smartjavaai-seetaface6-lib | seetaface6人脸算法JNI接口封装 |
可以根据需求对每个模块单独引入,也可以通过引入`smartjavaai-all`方式引入所有模块。
@@ -134,7 +133,7 @@
ink.numberone
smartjavaai-all
- 1.0.8
+ 1.0.10
```
### 3、完整示例代码
@@ -167,6 +166,10 @@
## 更新日志
+## [v1.0.10] - 2025-04-19
+- 兼容 SeetaFace6 在 Linux 系统下的运行
+- 新增全局缓存路径设置功能
+- 优化若干功能细节,提升稳定性与性能
## [v1.0.8] - 2025-04-13
- 新增目标检测功能
- 模型调用接口统一封装
diff --git a/pom.xml b/pom.xml
index 3630892..25be50a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,13 +6,12 @@
ink.numberone
smartjavaai-parent
- 1.0.8
+ 1.0.10
pom
SmartJavaAI
smartjavaai-face
smartjavaai-common
- smartjavaai-seetaface6-lib
smartjavaai-objectdetection
smartjavaai-all
@@ -37,13 +36,13 @@
ink.numberone
smartjavaai-common
- 1.0.8
+ 1.0.10
ink.numberone
smartjavaai-face
- 1.0.8
+ 1.0.10
@@ -185,6 +184,18 @@
runtime
+
+ cn.hutool
+ hutool-system
+ 5.8.16
+
+
+
+ cn.hutool
+ hutool-setting
+ 5.8.16
+
+
diff --git a/smartjavaai-all/pom.xml b/smartjavaai-all/pom.xml
index b3d4953..d21ec3f 100644
--- a/smartjavaai-all/pom.xml
+++ b/smartjavaai-all/pom.xml
@@ -6,11 +6,11 @@
ink.numberone
smartjavaai-parent
- 1.0.8
+ 1.0.10
smartjavaai-all
- 1.0.8
+ 1.0.10
smartjavaai-all
SmartJavaAI
https://github.com/geekwenjie/SmartJavaAI
diff --git a/smartjavaai-common/pom.xml b/smartjavaai-common/pom.xml
index b0ef058..eb93c5e 100644
--- a/smartjavaai-common/pom.xml
+++ b/smartjavaai-common/pom.xml
@@ -6,7 +6,7 @@
ink.numberone
smartjavaai-parent
- 1.0.8
+ 1.0.10
smartjavaai-common
diff --git a/smartjavaai-common/src/main/java/cn/smartjavaai/common/config/Config.java b/smartjavaai-common/src/main/java/cn/smartjavaai/common/config/Config.java
new file mode 100644
index 0000000..5a3cad0
--- /dev/null
+++ b/smartjavaai-common/src/main/java/cn/smartjavaai/common/config/Config.java
@@ -0,0 +1,84 @@
+package cn.smartjavaai.common.config;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.system.SystemUtil;
+import cn.hutool.system.UserInfo;
+import cn.smartjavaai.common.utils.FileUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+
+/**
+ * 全局配置
+ * @author dwj
+ * @date 2025/4/15
+ */
+@Slf4j
+public class Config {
+
+ /**
+ * 默认缓存文件夹
+ */
+ private static final String CACHE_DIR = "smartjavaai_cache";
+
+ private static String cachePath;
+
+ static{
+ createCachePath();
+ if(StringUtils.isNotBlank(cachePath)){
+ System.setProperty("DJL_CACHE_DIR", cachePath);
+ }
+ }
+
+ // 设置缓存路径的方法
+ public static void setCachePath(String customeCachePath) {
+ if (StringUtils.isNotBlank(customeCachePath)) {
+ /*if(!FileUtils.isValidDirectory(customeCachePath)){
+ throw new IllegalArgumentException("无效的缓存路径");
+ }*/
+ cachePath = customeCachePath;
+ FileUtil.mkdir(cachePath);
+ // 如果需要在此时直接设置系统属性
+ System.setProperty("DJL_CACHE_DIR", cachePath);
+ } else {
+ throw new IllegalArgumentException("缓存路径不允许为空");
+ }
+ }
+
+ // 获取缓存路径的方法
+ public static String getCachePath() {
+ if(StringUtils.isBlank(cachePath)){
+ createCachePath();
+ }
+ if(StringUtils.isNotBlank(cachePath)){
+ System.setProperty("DJL_CACHE_DIR", cachePath);
+ }
+ return cachePath;
+ }
+
+ // 获取当前缓存路径的系统属性(如果需要在其他地方使用)
+ public static String getCachePathFromSystem() {
+ return System.getProperty("DJL_CACHE_DIR");
+ }
+
+ private static void createCachePath(){
+ String osName = SystemUtil.getOsInfo().getName();
+ log.info("当前操作系统:{}", osName);
+ if(osName.toLowerCase().contains("windows")){
+ cachePath = SystemUtil.getUserInfo().getHomeDir() + CACHE_DIR;
+ FileUtil.mkdir(cachePath);
+ }else if(osName.toLowerCase().contains("linux")){
+ cachePath = "/root/" + CACHE_DIR;
+ FileUtil.mkdir(cachePath);
+ }else if(osName.toLowerCase().contains("mac")){
+ cachePath = SystemUtil.getUserInfo().getHomeDir() + CACHE_DIR;
+ FileUtil.mkdir(cachePath);
+ }else{
+ cachePath = SystemUtil.getUserInfo().getHomeDir() + CACHE_DIR;
+ FileUtil.mkdir(cachePath);
+ }
+ }
+
+
+}
diff --git a/smartjavaai-common/src/main/java/cn/smartjavaai/common/utils/FileUtils.java b/smartjavaai-common/src/main/java/cn/smartjavaai/common/utils/FileUtils.java
index 67afd6f..a3cef1d 100644
--- a/smartjavaai-common/src/main/java/cn/smartjavaai/common/utils/FileUtils.java
+++ b/smartjavaai-common/src/main/java/cn/smartjavaai/common/utils/FileUtils.java
@@ -18,4 +18,14 @@ public class FileUtils {
File file = new File(filePath);
return file.exists() && !file.isDirectory(); // 确保是文件且存在
}
+
+ /**
+ * 检查目录是否存在
+ * @param path
+ * @return
+ */
+ public static boolean isValidDirectory(String path) {
+ File file = new File(path);
+ return file.exists() && file.isDirectory();
+ }
}
diff --git a/smartjavaai-face/pom.xml b/smartjavaai-face/pom.xml
index 6bb03a7..cd6eebb 100644
--- a/smartjavaai-face/pom.xml
+++ b/smartjavaai-face/pom.xml
@@ -6,11 +6,11 @@
ink.numberone
smartjavaai-parent
- 1.0.8
+ 1.0.10
smartjavaai-face
- 1.0.8
+ 1.0.10
smartjavaai-face
SmartJavaAI
https://github.com/geekwenjie/SmartJavaAI
@@ -37,9 +37,9 @@
- ink.numberone
- smartjavaai-seetaface6-lib
- ${project.version}
+ io.gitee.dengwenjie
+ seeta-sdk-platform
+ 1.2.2
diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceConfig.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceConfig.java
index ef6b92f..231f6d5 100644
--- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceConfig.java
+++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceConfig.java
@@ -20,6 +20,11 @@ public class FaceConfig {
*/
public static final float NMS_THRESHOLD = 0.45F;
+ /**
+ * 默认相似度阈值
+ */
+ public static final float SEETAFACE_DEFAULT_SIMILARITY_THRESHOLD = 0.85F;
+
}
diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceModelConfig.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceModelConfig.java
index a527c95..9372527 100644
--- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceModelConfig.java
+++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceModelConfig.java
@@ -20,6 +20,11 @@ public class FaceModelConfig {
*/
private double confidenceThreshold = FaceConfig.DEFAULT_CONFIDENCE_THRESHOLD;
+ /**
+ * 相似度阈值 作用:判断是否为同一人脸
+ */
+ private double similarityThreshold = 0D;
+
/**
* 非极大抑制阈值 作用:消除重叠检测框,保留最优结果
*/
@@ -40,6 +45,11 @@ public class FaceModelConfig {
*/
private DeviceEnum device;
+ /**
+ * gpu设备ID 当device为GPU时生效
+ */
+ private int gpuId = 0;
+
}
diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceModelFactory.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceModelFactory.java
index 1474dbe..81d64bf 100644
--- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceModelFactory.java
+++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/FaceModelFactory.java
@@ -1,5 +1,6 @@
package cn.smartjavaai.face;
+import cn.smartjavaai.common.config.Config;
import cn.smartjavaai.face.exception.FaceException;
import cn.smartjavaai.face.model.FeatureExtractionModel;
import cn.smartjavaai.face.model.RetinaFaceModel;
@@ -122,6 +123,7 @@ public class FaceModelFactory {
//人脸特征提取
registerAlgorithm("featureextractionmodel", FeatureExtractionModel.class);
registerAlgorithm("seetaface6model", SeetaFace6Model.class);
+ log.info("缓存目录:{}", Config.getCachePath());
}
}
diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/dao/FaceDao.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/dao/FaceDao.java
index deaa1b6..e3acd7e 100644
--- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/dao/FaceDao.java
+++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/dao/FaceDao.java
@@ -45,7 +45,7 @@ public class FaceDao {
* @throws SQLException
* @throws ClassNotFoundException
*/
- public String findKeyByIndex(int index) throws SQLException, ClassNotFoundException {
+ public String findKeyByIndex(long index) throws SQLException, ClassNotFoundException {
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
return sqliteHelper.executeQuery("select \"key\" from " + TABLE_NAME_IMG + " where \"index\"=" + index);
}
diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/FeatureExtractionModel.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/FeatureExtractionModel.java
index af377c1..7cb5f06 100644
--- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/FeatureExtractionModel.java
+++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/FeatureExtractionModel.java
@@ -104,7 +104,6 @@ public class FeatureExtractionModel extends AbstractFaceModel implements AutoClo
if (predictor != null) {
try {
predictorPool.returnObject(predictor); //归还
- log.info("释放资源");
} catch (Exception e) {
log.warn("归还Predictor失败", e);
try {
diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/RetinaFaceModel.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/RetinaFaceModel.java
index f98dfea..718e2b7 100644
--- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/RetinaFaceModel.java
+++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/RetinaFaceModel.java
@@ -220,7 +220,6 @@ public class RetinaFaceModel extends AbstractFaceModel implements AutoCloseable{
if (predictor != null) {
try {
predictorPool.returnObject(predictor); //归还
- log.info("释放资源");
} catch (Exception e) {
log.warn("归还Predictor失败", e);
try {
diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/SeetaFace6Model.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/SeetaFace6Model.java
index 1030f2a..81bd42e 100644
--- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/SeetaFace6Model.java
+++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/SeetaFace6Model.java
@@ -1,6 +1,12 @@
package cn.smartjavaai.face.model;
+import ai.djl.inference.Predictor;
+import ai.djl.modality.cv.Image;
+import ai.djl.modality.cv.output.DetectedObjects;
+import cn.smartjavaai.common.config.Config;
import cn.smartjavaai.common.entity.DetectionResponse;
+import cn.smartjavaai.common.enums.DeviceEnum;
+import cn.smartjavaai.common.pool.PredictorFactory;
import cn.smartjavaai.common.utils.FileUtils;
import cn.smartjavaai.common.utils.ImageUtils;
import cn.smartjavaai.face.AbstractFaceModel;
@@ -10,13 +16,14 @@ import cn.smartjavaai.face.entity.FaceData;
import cn.smartjavaai.face.entity.FaceResult;
import cn.smartjavaai.face.exception.FaceException;
import cn.smartjavaai.face.utils.FaceUtils;
+import com.seeta.pool.*;
+import com.seeta.sdk.*;
import com.seetaface.NativeLoader;
import com.seetaface.SeetaFace6JNI;
-import com.seetaface.model.RecognizeResult;
-import com.seetaface.model.SeetaImageData;
-import com.seetaface.model.SeetaRect;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.pool2.ObjectPool;
+import org.apache.commons.pool2.impl.GenericObjectPool;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@@ -38,40 +45,76 @@ public class SeetaFace6Model extends AbstractFaceModel {
private FaceModelConfig config;
- private static final Object lock = new Object(); // 全局锁
+ private FaceDetectorPool faceDetectorPool;
+ private FaceRecognizerPool faceRecognizerPool;
+ private FaceLandmarkerPool faceLandmarkerPool;
+
+ private FaceDatabasePool faceDatabasePool;
+
+ /**
+ * 默认相似度阈值
+ */
+ public static final float SEETAFACE_DEFAULT_SIMILARITY_THRESHOLD = 0.62F;
@Override
public void loadModel(FaceModelConfig config) {
this.config = config;
- if (NativeLoader.seetaFace6SDK == null) {
- synchronized (lock) {
- if(StringUtils.isBlank(config.getModelPath())){
- throw new FaceException("modelPath is null");
- }
- //加载依赖库
- NativeLoader.loadNativeLibraries(config.getModelPath());
- log.info("Loading seetaFace6 library successfully.");
- NativeLoader.seetaFace6SDK = new SeetaFace6JNI();
- //加载模型
- boolean isSuccess = NativeLoader.seetaFace6SDK.initModel(config.getModelPath());
- if(!isSuccess){
- throw new FaceException("seetaFace6模型初始化失败," + config.getModelPath());
- }
- log.info("Load seetaFace6 model success!");
- new Thread(new Runnable() {
- public void run() {
- try {
- log.info("start load faceDb...");
- loadFaceDb();
- log.info("Load faceDb success!");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }).start();
+ if(StringUtils.isBlank(config.getModelPath())){
+ throw new FaceException("modelPath is null");
+ }
+ //设置默认相似度阈值
+ if(config.getSimilarityThreshold() <= 0){
+ config.setSimilarityThreshold(SEETAFACE_DEFAULT_SIMILARITY_THRESHOLD);
+ }
+ //加载依赖库
+ NativeLoader.loadNativeLibraries(config);
+ log.info("Loading seetaFace6 library successfully.");
+ String[] faceDetectorModelPath = {config.getModelPath() + File.separator + "face_detector.csta"};
+ String[] faceRecognizerModelPath = {config.getModelPath() + File.separator + "face_recognizer.csta"};
+ String[] faceLandmarkerModelPath = {config.getModelPath() + File.separator + "face_landmarker_pts5.csta"};
+ SeetaDevice device = SeetaDevice.SEETA_DEVICE_AUTO;
+ int gpuId = 0;
+ if(Objects.nonNull(config.getDevice())){
+ device = config.getDevice() == DeviceEnum.CPU ? SeetaDevice.SEETA_DEVICE_CPU : SeetaDevice.SEETA_DEVICE_GPU;
+ if(config.getGpuId() >= 0 && device == SeetaDevice.SEETA_DEVICE_GPU){
+ gpuId = config.getGpuId();
}
}
+ try {
+ SeetaModelSetting faceDetectorPoolSetting = new SeetaModelSetting(gpuId, faceDetectorModelPath, device);
+ SeetaConfSetting faceDetectorPoolConfSetting = new SeetaConfSetting(faceDetectorPoolSetting);
+
+ SeetaModelSetting faceRecognizerPoolSetting = new SeetaModelSetting(gpuId, faceRecognizerModelPath, device);
+ SeetaConfSetting faceRecognizerPoolConfSetting = new SeetaConfSetting(faceRecognizerPoolSetting);
+
+ SeetaModelSetting faceLandmarkerPoolSetting = new SeetaModelSetting(gpuId, faceLandmarkerModelPath, device);
+ SeetaConfSetting faceLandmarkerPoolConfSetting = new SeetaConfSetting(faceLandmarkerPoolSetting);
+
+ SeetaModelSetting faceDatabasePoolSetting = new SeetaModelSetting(gpuId, faceRecognizerModelPath, device);
+ SeetaConfSetting faceDatabasePoolConfSetting = new SeetaConfSetting(faceDatabasePoolSetting);
+
+ this.faceDetectorPool = new FaceDetectorPool(faceDetectorPoolConfSetting);
+ this.faceRecognizerPool = new FaceRecognizerPool(faceRecognizerPoolConfSetting);
+ this.faceLandmarkerPool = new FaceLandmarkerPool(faceLandmarkerPoolConfSetting);
+ this.faceDatabasePool = new FaceDatabasePool(faceDatabasePoolConfSetting);
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ log.info("start load faceDb...");
+ loadFaceDb();
+ log.info("Load faceDb success!");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ } catch (FileNotFoundException e) {
+ throw new FaceException(e);
+ }
+
}
@Override
@@ -110,9 +153,21 @@ public class SeetaFace6Model extends AbstractFaceModel {
}
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
- synchronized (lock) {
- SeetaRect[] seetaResult = NativeLoader.seetaFace6SDK.detect(imageData);
+ FaceDetector predictor = null;
+ try {
+ predictor = faceDetectorPool.borrowObject();
+ SeetaRect[] seetaResult = predictor.Detect(imageData);
return FaceUtils.convertToDetectionResponse(seetaResult, config);
+ } catch (Exception e) {
+ throw new FaceException("目标检测错误", e);
+ }finally {
+ if (predictor != null) {
+ try {
+ faceDetectorPool.returnObject(predictor); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
}
}
@@ -170,6 +225,76 @@ public class SeetaFace6Model extends AbstractFaceModel {
}
}
+ /**
+ * 获取5点坐标,循序依次为,左眼中心、右眼中心、鼻尖、左嘴角和右嘴角
+ * @param imageData
+ * @return
+ */
+ private SeetaPointF[] getMaskPoint(SeetaImageData imageData) {
+ FaceDetector faceDetector = null;
+ FaceLandmarker faceLandmarker = null;
+ try {
+ faceDetector = faceDetectorPool.borrowObject();
+ faceLandmarker = faceLandmarkerPool.borrowObject();
+ //检测人脸
+ SeetaRect[] seetaResult = faceDetector.Detect(imageData);
+ if(Objects.isNull(seetaResult) || seetaResult.length == 0){
+ throw new FaceException("未检测到人脸");
+ }
+ //提取第一个人脸的5点人脸标识
+ SeetaPointF[] pointFS = new SeetaPointF[faceLandmarker.number()];
+ faceLandmarker.mark(imageData, seetaResult[0], pointFS);
+ return pointFS;
+ } catch (FaceException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new FaceException("目标检测错误", e);
+ }finally {
+ if (faceDetector != null) {
+ try {
+ faceDetectorPool.returnObject(faceDetector); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
+ if (faceLandmarker != null) {
+ try {
+ faceLandmarkerPool.returnObject(faceLandmarker); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * 裁剪人脸
+ * @param imageData
+ * @return
+ */
+ private SeetaImageData getMaxCropFace(SeetaImageData imageData){
+ FaceRecognizer faceRecognizer = null;
+ try {
+ faceRecognizer = faceRecognizerPool.borrowObject();
+ //提取第一个人脸的5点人脸标识
+ SeetaPointF[] pointFS = getMaskPoint(imageData);
+ //裁剪人脸
+ SeetaImageData cropImageData = new SeetaImageData(faceRecognizer.GetCropFaceWidthV2(), faceRecognizer.GetCropFaceHeightV2(), faceRecognizer.GetCropFaceChannelsV2());
+ faceRecognizer.CropFaceV2(imageData, pointFS, cropImageData);
+ return cropImageData;
+ } catch (Exception e) {
+ throw new FaceException(e);
+ }finally {
+ if (faceRecognizer != null) {
+ try {
+ faceRecognizerPool.returnObject(faceRecognizer); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
+ }
+ }
+
@Override
public float[] featureExtraction(BufferedImage image) {
if(!ImageUtils.isImageValid(image)){
@@ -177,10 +302,56 @@ public class SeetaFace6Model extends AbstractFaceModel {
}
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
- synchronized (lock) {
- return NativeLoader.seetaFace6SDK.extractMaxFace(imageData);
- }
+ FaceDetector faceDetector = null;
+ FaceLandmarker faceLandmarker = null;
+ FaceRecognizer faceRecognizer = null;
+ try {
+ faceDetector = faceDetectorPool.borrowObject();
+ faceLandmarker = faceLandmarkerPool.borrowObject();
+ faceRecognizer = faceRecognizerPool.borrowObject();
+ //检测人脸
+ SeetaRect[] seetaResult = faceDetector.Detect(imageData);
+ if(Objects.isNull(seetaResult) || seetaResult.length == 0){
+ throw new FaceException("未检测到人脸");
+ }
+ //提取第一个人脸的5点人脸标识
+ SeetaPointF[] pointFS = new SeetaPointF[faceLandmarker.number()];
+ faceLandmarker.mark(imageData, seetaResult[0], pointFS);
+ //提取特征
+ float[] features = new float[faceRecognizer.GetExtractFeatureSize()];
+ boolean isSuccess = faceRecognizer.Extract(imageData, pointFS, features);
+ if(!isSuccess){
+ throw new FaceException("人脸特征提取失败");
+ }
+ return features;
+ } catch (FaceException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new FaceException("目标检测错误", e);
+ }finally {
+ if (faceDetector != null) {
+ try {
+ faceDetectorPool.returnObject(faceDetector); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
+ if (faceLandmarker != null) {
+ try {
+ faceLandmarkerPool.returnObject(faceLandmarker); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
+ if (faceRecognizer != null) {
+ try {
+ faceRecognizerPool.returnObject(faceRecognizer); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
+ }
}
@Override
@@ -228,8 +399,20 @@ public class SeetaFace6Model extends AbstractFaceModel {
if(Objects.isNull(feature1) || Objects.isNull(feature2)){
throw new FaceException("特征向量无效");
}
- synchronized (lock) {
- return NativeLoader.seetaFace6SDK.calculateSimilarity(feature1, feature2);
+ FaceRecognizer faceRecognizer = null;
+ try {
+ faceRecognizer = faceRecognizerPool.borrowObject();
+ return faceRecognizer.CalculateSimilarity(feature1, feature2);
+ } catch (Exception e) {
+ throw new FaceException(e);
+ }finally {
+ if (faceRecognizer != null) {
+ try {
+ faceRecognizerPool.returnObject(faceRecognizer); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
}
}
@@ -276,27 +459,85 @@ public class SeetaFace6Model extends AbstractFaceModel {
SeetaImageData imageData2 = new SeetaImageData(image2.getWidth(), image2.getHeight(), 3);
imageData2.data = ImageUtils.getMatrixBGR(image2);
- synchronized (lock) {
- //裁剪
- byte[][] cropImg1 = NativeLoader.seetaFace6SDK.crop(imageData1);
- byte[][] cropImg2 = NativeLoader.seetaFace6SDK.crop(imageData2);
- if(cropImg1 == null || cropImg1.length == 0){
- throw new FaceException("未发现人脸");
- }
- if(cropImg2 == null || cropImg2.length == 0){
- throw new FaceException("未发现人脸");
- }
- BufferedImage cropImage1 = ImageUtils.bgrToBufferedImage(cropImg1[0], 256, 256);
- BufferedImage cropImage2 = ImageUtils.bgrToBufferedImage(cropImg2[0], 256, 256);
- SeetaImageData cropImageData1 = new SeetaImageData(cropImage1.getWidth(), cropImage1.getHeight(), 3);
- cropImageData1.data = ImageUtils.getMatrixBGR(cropImage1);
- SeetaImageData cropImageData2 = new SeetaImageData(cropImage2.getWidth(), cropImage2.getHeight(), 3);
- cropImageData2.data = ImageUtils.getMatrixBGR(cropImage2);
- return NativeLoader.seetaFace6SDK.compare(cropImageData1, cropImageData2);
+
+ FaceRecognizer faceRecognizer = null;
+ FaceDatabase faceDatabase = null;
+ FaceLandmarker faceLandmarker = null;
+ FaceDetector faceDetector = null;
+ try {
+ faceRecognizer = faceRecognizerPool.borrowObject();
+ faceDatabase = faceDatabasePool.borrowObject();
+ faceLandmarker = faceLandmarkerPool.borrowObject();
+ faceDetector = faceDetectorPool.borrowObject();
+
+ //检测人脸
+ SeetaRect[] seetaResult = faceDetector.Detect(imageData1);
+ if(Objects.isNull(seetaResult) || seetaResult.length == 0){
+ throw new FaceException("未检测到人脸");
+ }
+ //提取第一个人脸的5点人脸标识
+ SeetaPointF[] pointFS1 = new SeetaPointF[faceLandmarker.number()];
+ faceLandmarker.mark(imageData1, seetaResult[0], pointFS1);
+
+ //裁剪人脸
+ SeetaImageData cropImageData1 = new SeetaImageData(faceRecognizer.GetCropFaceWidthV2(), faceRecognizer.GetCropFaceHeightV2(), faceRecognizer.GetCropFaceChannelsV2());
+ faceRecognizer.CropFaceV2(imageData1, pointFS1, cropImageData1);
+
+ //图片2:检测人脸
+ SeetaRect[] seetaResult2 = faceDetector.Detect(imageData2);
+ if(Objects.isNull(seetaResult2) || seetaResult2.length == 0){
+ throw new FaceException("未检测到人脸");
+ }
+ //图片2:提取第一个人脸的5点人脸标识
+ SeetaPointF[] pointFS2 = new SeetaPointF[faceLandmarker.number()];
+ faceLandmarker.mark(imageData2, seetaResult2[0], pointFS2);
+
+ //图片2:裁剪人脸
+ SeetaImageData cropImageData2 = new SeetaImageData(faceRecognizer.GetCropFaceWidthV2(), faceRecognizer.GetCropFaceHeightV2(), faceRecognizer.GetCropFaceChannelsV2());
+ faceRecognizer.CropFaceV2(imageData2, pointFS2, cropImageData2);
+
+ return faceDatabase.CompareByCroppedFace(cropImageData1, cropImageData2);
+ } catch (FaceException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new FaceException(e);
+ }finally {
+ if (faceDetector != null) {
+ try {
+ faceDetectorPool.returnObject(faceDetector); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
+ if (faceLandmarker != null) {
+ try {
+ faceLandmarkerPool.returnObject(faceLandmarker); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
+ if (faceRecognizer != null) {
+ try {
+ faceRecognizerPool.returnObject(faceRecognizer); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
+ if (faceDatabase != null) {
+ try {
+ faceDatabasePool.returnObject(faceDatabase); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
}
+
}
+
+
+
@Override
public float featureComparison(byte[] imageData1, byte[] imageData2) {
if(Objects.isNull(imageData1) || Objects.isNull(imageData2)){
@@ -338,13 +579,11 @@ public class SeetaFace6Model extends AbstractFaceModel {
}
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
- synchronized (lock) {
- byte[][] bytes = NativeLoader.seetaFace6SDK.crop(imageData);
- if (bytes == null || bytes.length == 0) {
- log.info("register face fail: key={}, error=no valid face", key);
- return false;
- }
- long index = NativeLoader.seetaFace6SDK.registerCroppedFace(bytes[0]);
+ FaceDatabase faceDatabase = null;
+ try {
+ faceDatabase = faceDatabasePool.borrowObject();
+ SeetaImageData cropImageData = getMaxCropFace(imageData);
+ long index = faceDatabase.RegisterByCroppedFace(cropImageData);
if (index < 0) {
log.info("register face fail: key={}, index={}", key, index);
return false;
@@ -353,13 +592,25 @@ public class SeetaFace6Model extends AbstractFaceModel {
FaceData face = new FaceData();
face.setKey(key);
face.setIndex(index);
- face.setImgData(bytes[0]);
+ face.setImgData(cropImageData.data);
try {
new FaceDao(config.getFaceDbPath()).save(face);
} catch (SQLException | ClassNotFoundException e) {
throw new FaceException("保存人脸库失败", e);
}
return true;
+ } catch (FaceException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new FaceException(e);
+ }finally {
+ if (faceDatabase != null) {
+ try {
+ faceDatabasePool.returnObject(faceDatabase); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
}
}
@@ -400,9 +651,13 @@ public class SeetaFace6Model extends AbstractFaceModel {
* @param faceData
* @return
*/
- private boolean register(String key, FaceData faceData) {
- synchronized (lock) {
- long index = NativeLoader.seetaFace6SDK.registerCroppedFace(faceData.getImgData());
+ private boolean registerCroppedFace(String key, FaceData faceData) {
+ FaceDatabase faceDatabase = null;
+ try {
+ faceDatabase = faceDatabasePool.borrowObject();
+ SeetaImageData cropImageData = new SeetaImageData(faceData.getWidth(), faceData.getHeight(), faceData.getChannel());
+ cropImageData.data = faceData.getImgData();
+ long index = faceDatabase.RegisterByCroppedFace(cropImageData);
if (index < 0) {
log.info("register face fail: key={}, index={}", key, index);
return false;
@@ -414,6 +669,18 @@ public class SeetaFace6Model extends AbstractFaceModel {
throw new FaceException(e);
}
return rows > 0;
+ } catch (FaceException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new FaceException(e);
+ }finally {
+ if (faceDatabase != null) {
+ try {
+ faceDatabasePool.returnObject(faceDatabase); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
}
}
@@ -457,9 +724,33 @@ public class SeetaFace6Model extends AbstractFaceModel {
}
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
imageData.data = ImageUtils.getMatrixBGR(image);
- synchronized (lock) {
- RecognizeResult recognizeResult = NativeLoader.seetaFace6SDK.query(imageData);
- return searchFaceDb(recognizeResult);
+ FaceDatabase faceDatabase = null;
+ try {
+ faceDatabase = faceDatabasePool.borrowObject();
+ SeetaPointF[] points = getMaskPoint(imageData);
+ long[] index = new long[1];
+ float[] similarity = new float[1];
+ long result = faceDatabase.QueryTop(imageData, points, 1, index, similarity);
+ if(result < 1){
+ return null;
+ }
+ //检查相似度
+ if(similarity[0] < config.getSimilarityThreshold()){
+ return null;
+ }
+ return searchFaceDb(index[0], similarity[0]);
+ } catch (FaceException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new FaceException(e);
+ }finally {
+ if (faceDatabase != null) {
+ try {
+ faceDatabasePool.returnObject(faceDatabase); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
}
}
@@ -485,21 +776,32 @@ public class SeetaFace6Model extends AbstractFaceModel {
if(!checkFaceDb()){
throw new FaceException("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
}
- synchronized (lock) {
- try {
- List list = new FaceDao(config.getFaceDbPath()).findIndexList(keys);
- if (list == null) {
- return 0;
+
+ FaceDatabase faceDatabase = null;
+ try {
+ List list = new FaceDao(config.getFaceDbPath()).findIndexList(keys);
+ if (list == null) {
+ return 0;
+ }
+ faceDatabase = faceDatabasePool.borrowObject();
+ int rows = 0;
+ for (long index : list) {
+ int row = faceDatabase.Delete(index);
+ rows += row;
+ }
+ new FaceDao(config.getFaceDbPath()).deleteFace(keys);
+ return rows;
+ } catch (Exception e) {
+ throw new FaceException(e);
+ }finally {
+ if (faceDatabase != null) {
+ try {
+ faceDatabasePool.returnObject(faceDatabase); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
}
- long[] array = list.stream().mapToLong(Long::longValue).toArray();
- long rows = NativeLoader.seetaFace6SDK.delete(array);
- new FaceDao(config.getFaceDbPath()).deleteFace(keys);
- return rows;
- } catch (SQLException | ClassNotFoundException e) {
- throw new FaceException(e);
}
}
-
}
@Override
@@ -507,16 +809,31 @@ public class SeetaFace6Model extends AbstractFaceModel {
if(!checkFaceDb()){
throw new FaceException("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
}
- synchronized (lock) {
- long rows = NativeLoader.seetaFace6SDK.delete(new long[]{-1});
+
+ FaceDatabase faceDatabase = null;
+ try {
+ faceDatabase = faceDatabasePool.borrowObject();
+ faceDatabase.Clear();
+ long rows = 0;
try {
- new FaceDao(config.getFaceDbPath()).deleteAll();
+ rows = new FaceDao(config.getFaceDbPath()).deleteAll();
} catch (SQLException | ClassNotFoundException e) {
throw new FaceException("删除人脸库失败", e);
}
return rows;
+ } catch (FaceException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new FaceException(e);
+ }finally {
+ if (faceDatabase != null) {
+ try {
+ faceDatabasePool.returnObject(faceDatabase); //归还
+ } catch (Exception e) {
+ log.warn("归还Predictor失败", e);
+ }
+ }
}
-
}
/**
@@ -531,17 +848,15 @@ public class SeetaFace6Model extends AbstractFaceModel {
return false;
}
- private FaceResult searchFaceDb(RecognizeResult recognizeResult) {
- if(recognizeResult != null && recognizeResult.index >= 0){
+ private FaceResult searchFaceDb(long index,float similar) {
+ if(index >= 0){
String key = null;
- synchronized (lock) {
- try {
- key = new FaceDao(config.getFaceDbPath()).findKeyByIndex(recognizeResult.index);
- } catch (SQLException | ClassNotFoundException e) {
- throw new FaceException("查询人脸库失败", e);
- }
- return new FaceResult(key, recognizeResult.similar);
+ try {
+ key = new FaceDao(config.getFaceDbPath()).findKeyByIndex(index);
+ } catch (SQLException | ClassNotFoundException e) {
+ throw new FaceException("查询人脸库失败", e);
}
+ return new FaceResult(key, similar);
}
return null;
}
@@ -571,7 +886,7 @@ public class SeetaFace6Model extends AbstractFaceModel {
}
list.forEach(face -> {
try {
- register(face.getKey(), face);
+ registerCroppedFace(face.getKey(), face);
} catch (Exception e) {
e.printStackTrace();
}
diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/UltraLightFastGenericFaceModel.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/UltraLightFastGenericFaceModel.java
index 4c83a6a..7bce773 100644
--- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/UltraLightFastGenericFaceModel.java
+++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/model/UltraLightFastGenericFaceModel.java
@@ -209,7 +209,6 @@ public class UltraLightFastGenericFaceModel extends AbstractFaceModel implements
if (predictor != null) {
try {
predictorPool.returnObject(predictor); //归还
- log.info("释放资源");
} catch (Exception e) {
log.warn("归还Predictor失败", e);
try {
diff --git a/smartjavaai-face/src/main/java/cn/smartjavaai/face/utils/FaceUtils.java b/smartjavaai-face/src/main/java/cn/smartjavaai/face/utils/FaceUtils.java
index 692a2d0..4f8c0c7 100644
--- a/smartjavaai-face/src/main/java/cn/smartjavaai/face/utils/FaceUtils.java
+++ b/smartjavaai-face/src/main/java/cn/smartjavaai/face/utils/FaceUtils.java
@@ -9,7 +9,7 @@ import cn.smartjavaai.common.entity.DetectionRectangle;
import cn.smartjavaai.common.utils.ImageUtils;
import cn.smartjavaai.face.FaceModelConfig;
import cn.smartjavaai.face.exception.FaceException;
-import com.seetaface.model.SeetaRect;
+import com.seeta.sdk.SeetaRect;
import javax.imageio.ImageIO;
import java.awt.*;
@@ -74,10 +74,10 @@ public class FaceUtils {
List rectangleList = new ArrayList();
for(SeetaRect rect : seetaResult){
//过滤置信度
- if(config.getConfidenceThreshold() > 0 && rect.score < config.getConfidenceThreshold()){
+ /*if(config.getConfidenceThreshold() > 0){
continue;
- }
- DetectionRectangle rectangle = new DetectionRectangle(rect.x, rect.y, rect.width, rect.height, rect.score);
+ }*/
+ DetectionRectangle rectangle = new DetectionRectangle(rect.x, rect.y, rect.width, rect.height, 0);
rectangleList.add(rectangle);
}
detectionResponse.setRectangleList(rectangleList);
diff --git a/smartjavaai-face/src/main/java/com/seetaface/NativeLoader.java b/smartjavaai-face/src/main/java/com/seetaface/NativeLoader.java
index 0e60b46..a3c912b 100644
--- a/smartjavaai-face/src/main/java/com/seetaface/NativeLoader.java
+++ b/smartjavaai-face/src/main/java/com/seetaface/NativeLoader.java
@@ -1,6 +1,17 @@
package com.seetaface;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.setting.dialect.Props;
+import cn.hutool.system.OsInfo;
+import cn.hutool.system.SystemUtil;
+import cn.smartjavaai.common.config.Config;
+import cn.smartjavaai.common.enums.DeviceEnum;
+import cn.smartjavaai.face.FaceModelConfig;
+import cn.smartjavaai.face.exception.FaceException;
+import com.seeta.sdk.SeetaDevice;
+import com.seeta.sdk.util.DllItem;
+import com.seeta.sdk.util.LoadNativeCore;
import jdk.dynalink.linker.support.Lookup;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -12,7 +23,10 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
+import java.util.*;
+import java.util.stream.Collectors;
/**
* 依赖库加载器
@@ -22,122 +36,202 @@ import java.nio.file.StandardCopyOption;
public class NativeLoader {
- private static Path tempNativeDir;
+ private static Path seetaface6NativePath;
private static final String[] WIN_LIBS = {"tennis.dll","tennis_haswell.dll","tennis_pentium.dll","tennis_sandy_bridge.dll","SeetaAuthorize.dll","SeetaFaceAntiSpoofingX600.dll","SeetaFaceDetector600.dll","SeetaFaceLandmarker600.dll","SeetaFaceRecognizer610.dll","SeetaFace6JNI.dll"};
//private static final String[] WIN_LIBS = {"tennis","tennis_haswell","tennis_pentium","tennis_sandy_bridge","SeetaAuthorize","SeetaFaceAntiSpoofingX600","SeetaFaceDetector600","SeetaFaceLandmarker600","SeetaFaceRecognizer610","SeetaFace6JNI"};
private static final String[] LINUX_CENTOS_LIBS = {"libSeetaAuthorize.so","libtennis.so","libtennis_haswell.so","libtennis_pentium.so","libtennis_sandy_bridge.so","libSeetaFaceDetector600.so","libSeetaAgePredictor600.so","libSeetaEyeStateDetector200.so","libSeetaFaceAntiSpoofingX600.so","libSeetaFaceLandmarker600.so","libSeetaFaceRecognizer610.so","libSeetaGenderPredictor600.so","libSeetaMaskDetector200.so","libSeetaPoseEstimation600.so","libSeetaFaceTracking600.so","libSeetaQualityAssessor300.so"};
private static final String[] LINUX_UBUNTU_LIBS = {"libSeetaAuthorize.so","libtennis.so","libtennis_haswell.so","libtennis_pentium.so","libtennis_sandy_bridge.so","libSeetaFaceDetector600.so","libSeetaAgePredictor600.so","libSeetaEyeStateDetector200.so","libSeetaFaceAntiSpoofingX600.so","libSeetaFaceLandmarker600.so","libSeetaFaceRecognizer610.so","libSeetaGenderPredictor600.so","libSeetaMaskDetector200.so","libSeetaPoseEstimation600.so","libSeetaFaceTracking600.so","libSeetaQualityAssessor300.so"};
- private static final String TEMP_DIR = "smartjavaai-native-libs";
+ private static final String SEETAFACE_LIB_DIR = "seetaface6";
public static SeetaFace6JNI seetaFace6SDK;
+ public static final String AMD64 = "amd64";
+
+ public static final String x86_64 = "amd64";
+
+ /**
+ * 定义dll 路径和加载顺序的文件
+ */
+ private static final String PROPERTIES_FILE_NAME = "dll.properties";
- public static void loadNativeLibraries(String modelPath) {
+
+ public static void loadNativeLibraries(FaceModelConfig config) {
try {
- // 创建临时目录
- tempNativeDir = Files.createTempDirectory(TEMP_DIR);
- log.info("create temp native directory: " + tempNativeDir.toAbsolutePath().toString());
-
- // 获取当前平台库列表
- String libDir = getLibDir();
- String[] libNames = getPlatformLibs(libDir);
-
- // 批量提取库文件
- for (String libName : libNames) {
- extractLibrary(libName,libDir);
+ OsInfo osInfo = SystemUtil.getOsInfo();
+ //检查当前系统是否支持
+ if(!osInfo.isWindows() && !osInfo.isLinux()){
+ throw new FaceException("当前系统不支持:" + osInfo.getName());
}
-
- String separator = System.getProperty("path.separator");
- String sysLib = System.getProperty("java.library.path");
- if (sysLib.endsWith(separator)) {
- System.setProperty("java.library.path", sysLib + tempNativeDir);
- } else {
- System.setProperty("java.library.path", sysLib + separator + tempNativeDir);
+ //判断硬件架构是否支持GPU
+ if(config.getDevice() != null && config.getDevice().equals(DeviceEnum.GPU)){
+ //GPU仅支持amd64
+ if(!osInfo.getArch().contains("amd64") && !osInfo.getArch().contains("x86_64")){
+ throw new FaceException("seetaface6 GPU模型不支持当前arch:" + osInfo.getArch());
+ }
}
-
-
- // 按顺序加载库(确保依赖关系)
- for (String libName : libNames) {
- log.info("Loading library: " + tempNativeDir + File.separator + libName);
- System.load(tempNativeDir + File.separator + libName);
+ seetaface6NativePath = Paths.get(Config.getCachePath(), SEETAFACE_LIB_DIR);
+ //创建目录
+ FileUtil.mkdir(seetaface6NativePath);
+ log.info("seetaface6依赖库路径: " + seetaface6NativePath.toAbsolutePath().toString());
+ //拷贝依赖库到缓存目录
+ List fileList = getLibFiles(osInfo, config.getDevice());
+ if(fileList != null && !fileList.isEmpty()){
+ // 加载依赖库文件
+ fileList.forEach(file -> {
+ System.load(file.getAbsolutePath());
+ log.info(String.format("load %s finish", file.getAbsolutePath()));
+ });
}
} catch (Exception e) {
throw new RuntimeException("Native library loading failed", e);
}
}
-
- private static String[] getPlatformLibs(String libDir) {
- if (libDir.contains("windows")) return WIN_LIBS;
- if (libDir.contains("centos")) return LINUX_CENTOS_LIBS;
- if (libDir.contains("ubuntu")) return LINUX_UBUNTU_LIBS;
- throw new UnsupportedOperationException("Unsupported OS");
+ /**
+ * 拷贝依赖库到缓存目录
+ * @param osInfo
+ * @return
+ */
+ private static List getLibFiles(OsInfo osInfo,DeviceEnum deviceEnum){
+ try {
+ String device = getDevice(deviceEnum);
+ log.info("当前设备:{}", device);
+ //获取dll文件列表
+ List baseList = new ArrayList<>();
+ List jniList = new ArrayList<>();
+ InputStream propsInputStream = LoadNativeCore.class.getResourceAsStream(getPropertiesPath());
+ Props props = new Props();
+ props.load(propsInputStream);
+ String prefix = getPrefix();
+ props.forEach((keyObj, valuObj) -> {
+ String key = (String) keyObj;
+ String value = (String) valuObj;
+ DllItem dllItem = new DllItem();
+ dllItem.setKey(key);
+ if (key.contains("base")) {
+ if (value.contains("tennis")) {
+ dllItem.setValue(prefix + "base/" + device + "/" + value);
+ } else {
+ dllItem.setValue(prefix + "base/" + value);
+ }
+ baseList.add(dllItem);
+ } else {
+ dllItem.setValue(prefix + value);
+ jniList.add(dllItem);
+ }
+ });
+ //给dll文件排序
+ List basePath = getSortedPath(baseList);
+ List sdkPath = getSortedPath(jniList);
+ List fileList = new ArrayList<>();
+ //拷贝文件到临时目录
+ for (String baseSo : basePath) {
+ fileList.add(extractLibrary(baseSo));
+ }
+ for (String sdkSo : sdkPath) {
+ fileList.add(extractLibrary(sdkSo));
+ }
+ return fileList;
+ } catch (Exception e) {
+ throw new FaceException("拷贝依赖库失败",e);
+ }
}
+ private static String getDevice(DeviceEnum deviceEnum) {
+ String device = "CPU";
+ if ("amd64".equals(getArch()) && deviceEnum != null) {
+ device = deviceEnum == DeviceEnum.GPU ? "GPU" : "CPU";
+ }
+ return device;
+ }
+
+
+ /**
+ * 返回路径文件前缀
+ *
+ * @return
+ */
+ private static String getPrefix() {
+ String arch = getArch();
+ //aarch64
+ String os = SystemUtil.getOsInfo().getName();
+ //Windows操作系统
+ if (os != null && os.toLowerCase().startsWith("windows")) {
+ os = "/windows/";
+ } else if (os != null && os.toLowerCase().startsWith("linux")) {//Linux操作系统
+ os = "/linux/";
+ } else { //其它操作系统
+ //安卓 乌班图等等,先不写
+ return null;
+ }
+ // "/seetaface6/windows/amd64"
+ return "/" + SEETAFACE_LIB_DIR + os + arch + "/";
+ }
+
+ private static String getArch() {
+ String arch = SystemUtil.getOsInfo().getArch().toLowerCase();
+ if (arch.startsWith("amd64")
+ || arch.startsWith("x86_64")
+ || arch.startsWith("x86-64")
+ || arch.startsWith("x64")) {
+ arch = "amd64";
+ } else if (arch.contains("aarch")) {
+ arch = "aarch64";
+ } else if (arch.contains("arm")) {
+ arch = "arm";
+ }
+ return arch;
+ }
+
+ /**
+ * 获取dll配置文件路径
+ *
+ * @return String
+ */
+ private static String getPropertiesPath() {
+ return getPrefix() + PROPERTIES_FILE_NAME;
+ }
+
+
+
/**
* 拷贝依赖库到临时目录
- * @param libName
- * @param libDir
+ * @param libPath
+ * @return
* @throws IOException
*/
- private static void extractLibrary(String libName,String libDir) throws IOException {
- String resourcePath = "/native" + libDir + "/" + libName;
- try (InputStream in = NativeLoader.class.getResourceAsStream(resourcePath)) {
+ private static File extractLibrary(String libPath) throws IOException {
+ String resourcePath = libPath;
+ try (InputStream in = com.seetaface.NativeLoader.class.getResourceAsStream(resourcePath)) {
if (in == null) throw new FileNotFoundException(resourcePath);
-
- Path targetPath = tempNativeDir.resolve(libName);
+ Path path = Paths.get(resourcePath);
+ String fileName = path.getFileName().toString();
+ Path targetPath = seetaface6NativePath.resolve(fileName);
Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING);
- log.info("copy target path success : " + targetPath.toAbsolutePath().toString());
-
+ log.info("copy target path success : {}", targetPath.toAbsolutePath().toString());
// 设置可执行权限
- if (!System.getProperty("os.name").toLowerCase().contains("win")) {
+ if (!SystemUtil.getOsInfo().getName().toLowerCase().contains("win")) {
targetPath.toFile().setExecutable(true);
}
+ return targetPath.toFile();
}
}
- /**
- * 获取依赖库目录
- * @return
- */
- private static String getLibDir() {
- String osName = System.getProperty("os.name").toLowerCase();
- if (osName.contains("win")) {
- return "/windows";
- } /*else if (osName.contains("linux")) {
- String linuxOsName = getLinuxOsName();
- if(StringUtils.isBlank(linuxOsName)){
- throw new UnsupportedOperationException("Unsupported platform");
- };
- if(linuxOsName.contains("ubuntu")){
- return "/linux/ubuntu";
- }else if(linuxOsName.contains("centos")){
- return "/linux/centos";
- }
- }*/
- throw new UnsupportedOperationException("Unsupported platform");
- }
-
-
/**
- * 获取linux系统名称
- * @return
+ * 将获得的配置进行排序 并生成路径
+ *
+ * @param list
+ * @return List
*/
- private static String getLinuxOsName(){
- try (BufferedReader reader = new BufferedReader(new FileReader("/etc/os-release"))) {
- String line;
- while ((line = reader.readLine()) != null) {
- if (line.startsWith("ID=")) {
- String distro = line.substring(3).replace("\"", "").trim();
- return distro;
- }
- }
- } catch (IOException e) {
- System.out.println("Failed to read /etc/os-release: " + e.getMessage());
- }
- return null;
+ private static List getSortedPath(List list) {
+ return list.stream().sorted(Comparator.comparing(dllItem -> {
+ int i = dllItem.getKey().lastIndexOf(".") + 1;
+ String substring = dllItem.getKey().substring(i);
+ return Integer.valueOf(substring);
+ })).map(DllItem::getValue).collect(Collectors.toList());
}
+
}
diff --git a/smartjavaai-objectdetection/pom.xml b/smartjavaai-objectdetection/pom.xml
index bc29850..d160f31 100644
--- a/smartjavaai-objectdetection/pom.xml
+++ b/smartjavaai-objectdetection/pom.xml
@@ -6,11 +6,11 @@
ink.numberone
smartjavaai-parent
- 1.0.8
+ 1.0.10
smartjavaai-objectdetection
- 1.0.8
+ 1.0.10
smartjavaai-objectdetection
SmartJavaAI
https://github.com/geekwenjie/SmartJavaAI
diff --git a/smartjavaai-objectdetection/src/main/java/cn/smartjavaai/objectdetection/model/DetectorModel.java b/smartjavaai-objectdetection/src/main/java/cn/smartjavaai/objectdetection/model/DetectorModel.java
index 307aa92..e2172c1 100644
--- a/smartjavaai-objectdetection/src/main/java/cn/smartjavaai/objectdetection/model/DetectorModel.java
+++ b/smartjavaai-objectdetection/src/main/java/cn/smartjavaai/objectdetection/model/DetectorModel.java
@@ -99,7 +99,7 @@ public class DetectorModel implements AutoCloseable{
/**
* 目标检测-将检测结果绘制到原图
* @param imagePath
- * @return
+ * @param outputPath
*/
public void detectAndDraw(String imagePath, String outputPath){
if(!FileUtils.isFileExists(imagePath)){
diff --git a/smartjavaai-objectdetection/src/main/java/cn/smartjavaai/objectdetection/model/ObjectDetectionModelFactory.java b/smartjavaai-objectdetection/src/main/java/cn/smartjavaai/objectdetection/model/ObjectDetectionModelFactory.java
index 48c9d23..f41a3b9 100644
--- a/smartjavaai-objectdetection/src/main/java/cn/smartjavaai/objectdetection/model/ObjectDetectionModelFactory.java
+++ b/smartjavaai-objectdetection/src/main/java/cn/smartjavaai/objectdetection/model/ObjectDetectionModelFactory.java
@@ -1,5 +1,6 @@
package cn.smartjavaai.objectdetection.model;
+import cn.smartjavaai.common.config.Config;
import cn.smartjavaai.objectdetection.DetectorModelConfig;
import cn.smartjavaai.objectdetection.DetectorModelEnum;
import cn.smartjavaai.objectdetection.exception.DetectionException;
@@ -21,6 +22,10 @@ public class ObjectDetectionModelFactory {
private static final ConcurrentHashMap modelMap = new ConcurrentHashMap<>();
+ static{
+ log.info("缓存目录:{}", Config.getCachePath());
+ }
+
// 私有构造函数,防止外部创建实例
private ObjectDetectionModelFactory() {}
diff --git a/smartjavaai-seetaface6-lib/pom.xml b/smartjavaai-seetaface6-lib/pom.xml
deleted file mode 100644
index 865a063..0000000
--- a/smartjavaai-seetaface6-lib/pom.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
- 4.0.0
-
- ink.numberone
- smartjavaai-parent
- 1.0.8
-
-
- smartjavaai-seetaface6-lib
-
-
- 11
- 11
- UTF-8
-
-
- SmartJavaAI
- https://github.com/geekwenjie/SmartJavaAI
-
-
- MIT License
- https://opensource.org/licenses/MIT
-
-
-
-
-
-
-
- org.sonatype.central
- central-publishing-maven-plugin
- 0.4.0
- true
-
- dengwenjie
- true
- ${project.groupId}:${project.artifactId}:${project.version}
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 3.1.0
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 3.1.0
-
- ${java.home}/bin/javadoc
- none
-
- -Xdoclint:none
-
-
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 3.1.0
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
-
-
-
-
- scm:git:git://github.com/geekwenjie/SmartJavaAI.git
- scm:git:ssh://github.com/geekwenjie/SmartJavaAI.git
- http://github.com/geekwenjie/SmartJavaAI/tree/master
-
-
-
-
-
- dengwenjie
- https://s01.oss.sonatype.org/content/repositories/snapshots
-
-
- dengwenjie
- https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-
-
-
- dengwenjie
- 775747758@qq.com
-
- Project Manager
- Architect
-
-
-
-
-
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaAuthorize.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaAuthorize.dll
deleted file mode 100644
index 69b5554..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaAuthorize.dll and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFace6JNI.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFace6JNI.dll
deleted file mode 100644
index 0a12041..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFace6JNI.dll and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFace6JNI.lib b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFace6JNI.lib
deleted file mode 100644
index 567cfac..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFace6JNI.lib and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceAntiSpoofingX600.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceAntiSpoofingX600.dll
deleted file mode 100644
index da1492f..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceAntiSpoofingX600.dll and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceAntiSpoofingX600.lib b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceAntiSpoofingX600.lib
deleted file mode 100644
index e988c24..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceAntiSpoofingX600.lib and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceDetector600.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceDetector600.dll
deleted file mode 100644
index 0ef7d2b..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceDetector600.dll and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceDetector600.lib b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceDetector600.lib
deleted file mode 100644
index 3eae7d3..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceDetector600.lib and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceLandmarker600.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceLandmarker600.dll
deleted file mode 100644
index 1fed8c1..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceLandmarker600.dll and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceLandmarker600.lib b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceLandmarker600.lib
deleted file mode 100644
index 052ea01..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceLandmarker600.lib and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceRecognizer610.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceRecognizer610.dll
deleted file mode 100644
index 5a2c20c..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceRecognizer610.dll and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceRecognizer610.lib b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceRecognizer610.lib
deleted file mode 100644
index 2a54e36..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/SeetaFaceRecognizer610.lib and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis.dll
deleted file mode 100644
index 2ddaabe..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis.dll and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis_haswell.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis_haswell.dll
deleted file mode 100644
index 87107b7..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis_haswell.dll and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis_pentium.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis_pentium.dll
deleted file mode 100644
index 4d34908..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis_pentium.dll and /dev/null differ
diff --git a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis_sandy_bridge.dll b/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis_sandy_bridge.dll
deleted file mode 100644
index 3e2fe78..0000000
Binary files a/smartjavaai-seetaface6-lib/src/main/resources/native/windows/tennis_sandy_bridge.dll and /dev/null differ