mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-10 03:28:49 +00:00
1、seetaface6兼容linux系统
2、支持设置全局缓存路径 3、优化部分功能
This commit is contained in:
@@ -111,7 +111,6 @@
|
|||||||
| smartjavaai-common | 基础通用模块,封装了公共功能,供各算法模块共享使用 |
|
| smartjavaai-common | 基础通用模块,封装了公共功能,供各算法模块共享使用 |
|
||||||
| smartjavaai-face | 人脸功能模块 |
|
| smartjavaai-face | 人脸功能模块 |
|
||||||
| smartjavaai-objectdetection | 目标检测模块 |
|
| smartjavaai-objectdetection | 目标检测模块 |
|
||||||
| smartjavaai-seetaface6-lib | seetaface6人脸算法JNI接口封装 |
|
|
||||||
|
|
||||||
可以根据需求对每个模块单独引入,也可以通过引入`smartjavaai-all`方式引入所有模块。
|
可以根据需求对每个模块单独引入,也可以通过引入`smartjavaai-all`方式引入所有模块。
|
||||||
|
|
||||||
@@ -134,7 +133,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ink.numberone</groupId>
|
<groupId>ink.numberone</groupId>
|
||||||
<artifactId>smartjavaai-all</artifactId>
|
<artifactId>smartjavaai-all</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
### 3、完整示例代码
|
### 3、完整示例代码
|
||||||
@@ -167,6 +166,10 @@
|
|||||||
## 更新日志
|
## 更新日志
|
||||||
|
|
||||||
|
|
||||||
|
## [v1.0.10] - 2025-04-19
|
||||||
|
- 兼容 SeetaFace6 在 Linux 系统下的运行
|
||||||
|
- 新增全局缓存路径设置功能
|
||||||
|
- 优化若干功能细节,提升稳定性与性能
|
||||||
## [v1.0.8] - 2025-04-13
|
## [v1.0.8] - 2025-04-13
|
||||||
- 新增目标检测功能
|
- 新增目标检测功能
|
||||||
- 模型调用接口统一封装
|
- 模型调用接口统一封装
|
||||||
|
|||||||
19
pom.xml
19
pom.xml
@@ -6,13 +6,12 @@
|
|||||||
|
|
||||||
<groupId>ink.numberone</groupId>
|
<groupId>ink.numberone</groupId>
|
||||||
<artifactId>smartjavaai-parent</artifactId>
|
<artifactId>smartjavaai-parent</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<description>SmartJavaAI</description>
|
<description>SmartJavaAI</description>
|
||||||
<modules>
|
<modules>
|
||||||
<module>smartjavaai-face</module>
|
<module>smartjavaai-face</module>
|
||||||
<module>smartjavaai-common</module>
|
<module>smartjavaai-common</module>
|
||||||
<module>smartjavaai-seetaface6-lib</module>
|
|
||||||
<module>smartjavaai-objectdetection</module>
|
<module>smartjavaai-objectdetection</module>
|
||||||
<module>smartjavaai-all</module>
|
<module>smartjavaai-all</module>
|
||||||
</modules>
|
</modules>
|
||||||
@@ -37,13 +36,13 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ink.numberone</groupId>
|
<groupId>ink.numberone</groupId>
|
||||||
<artifactId>smartjavaai-common</artifactId>
|
<artifactId>smartjavaai-common</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ink.numberone</groupId>
|
<groupId>ink.numberone</groupId>
|
||||||
<artifactId>smartjavaai-face</artifactId>
|
<artifactId>smartjavaai-face</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
@@ -185,6 +184,18 @@
|
|||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-system</artifactId>
|
||||||
|
<version>5.8.16</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-setting</artifactId>
|
||||||
|
<version>5.8.16</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>ink.numberone</groupId>
|
<groupId>ink.numberone</groupId>
|
||||||
<artifactId>smartjavaai-parent</artifactId>
|
<artifactId>smartjavaai-parent</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>smartjavaai-all</artifactId>
|
<artifactId>smartjavaai-all</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
<name>smartjavaai-all</name>
|
<name>smartjavaai-all</name>
|
||||||
<description>SmartJavaAI</description>
|
<description>SmartJavaAI</description>
|
||||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>ink.numberone</groupId>
|
<groupId>ink.numberone</groupId>
|
||||||
<artifactId>smartjavaai-parent</artifactId>
|
<artifactId>smartjavaai-parent</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>smartjavaai-common</artifactId>
|
<artifactId>smartjavaai-common</artifactId>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,4 +18,14 @@ public class FileUtils {
|
|||||||
File file = new File(filePath);
|
File file = new File(filePath);
|
||||||
return file.exists() && !file.isDirectory(); // 确保是文件且存在
|
return file.exists() && !file.isDirectory(); // 确保是文件且存在
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查目录是否存在
|
||||||
|
* @param path
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean isValidDirectory(String path) {
|
||||||
|
File file = new File(path);
|
||||||
|
return file.exists() && file.isDirectory();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>ink.numberone</groupId>
|
<groupId>ink.numberone</groupId>
|
||||||
<artifactId>smartjavaai-parent</artifactId>
|
<artifactId>smartjavaai-parent</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>smartjavaai-face</artifactId>
|
<artifactId>smartjavaai-face</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
<name>smartjavaai-face</name>
|
<name>smartjavaai-face</name>
|
||||||
<description>SmartJavaAI</description>
|
<description>SmartJavaAI</description>
|
||||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||||
@@ -37,9 +37,9 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ink.numberone</groupId>
|
<groupId>io.gitee.dengwenjie</groupId>
|
||||||
<artifactId>smartjavaai-seetaface6-lib</artifactId>
|
<artifactId>seeta-sdk-platform</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>1.2.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ public class FaceConfig {
|
|||||||
*/
|
*/
|
||||||
public static final float NMS_THRESHOLD = 0.45F;
|
public static final float NMS_THRESHOLD = 0.45F;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认相似度阈值
|
||||||
|
*/
|
||||||
|
public static final float SEETAFACE_DEFAULT_SIMILARITY_THRESHOLD = 0.85F;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ public class FaceModelConfig {
|
|||||||
*/
|
*/
|
||||||
private double confidenceThreshold = FaceConfig.DEFAULT_CONFIDENCE_THRESHOLD;
|
private double confidenceThreshold = FaceConfig.DEFAULT_CONFIDENCE_THRESHOLD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 相似度阈值 作用:判断是否为同一人脸
|
||||||
|
*/
|
||||||
|
private double similarityThreshold = 0D;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 非极大抑制阈值 作用:消除重叠检测框,保留最优结果
|
* 非极大抑制阈值 作用:消除重叠检测框,保留最优结果
|
||||||
*/
|
*/
|
||||||
@@ -40,6 +45,11 @@ public class FaceModelConfig {
|
|||||||
*/
|
*/
|
||||||
private DeviceEnum device;
|
private DeviceEnum device;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gpu设备ID 当device为GPU时生效
|
||||||
|
*/
|
||||||
|
private int gpuId = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package cn.smartjavaai.face;
|
package cn.smartjavaai.face;
|
||||||
|
|
||||||
|
import cn.smartjavaai.common.config.Config;
|
||||||
import cn.smartjavaai.face.exception.FaceException;
|
import cn.smartjavaai.face.exception.FaceException;
|
||||||
import cn.smartjavaai.face.model.FeatureExtractionModel;
|
import cn.smartjavaai.face.model.FeatureExtractionModel;
|
||||||
import cn.smartjavaai.face.model.RetinaFaceModel;
|
import cn.smartjavaai.face.model.RetinaFaceModel;
|
||||||
@@ -122,6 +123,7 @@ public class FaceModelFactory {
|
|||||||
//人脸特征提取
|
//人脸特征提取
|
||||||
registerAlgorithm("featureextractionmodel", FeatureExtractionModel.class);
|
registerAlgorithm("featureextractionmodel", FeatureExtractionModel.class);
|
||||||
registerAlgorithm("seetaface6model", SeetaFace6Model.class);
|
registerAlgorithm("seetaface6model", SeetaFace6Model.class);
|
||||||
|
log.info("缓存目录:{}", Config.getCachePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class FaceDao {
|
|||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
* @throws ClassNotFoundException
|
* @throws ClassNotFoundException
|
||||||
*/
|
*/
|
||||||
public String findKeyByIndex(int index) throws SQLException, ClassNotFoundException {
|
public String findKeyByIndex(long index) throws SQLException, ClassNotFoundException {
|
||||||
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
|
SqliteHelper sqliteHelper = new SqliteHelper(dbFilePath);
|
||||||
return sqliteHelper.executeQuery("select \"key\" from " + TABLE_NAME_IMG + " where \"index\"=" + index);
|
return sqliteHelper.executeQuery("select \"key\" from " + TABLE_NAME_IMG + " where \"index\"=" + index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ public class FeatureExtractionModel extends AbstractFaceModel implements AutoClo
|
|||||||
if (predictor != null) {
|
if (predictor != null) {
|
||||||
try {
|
try {
|
||||||
predictorPool.returnObject(predictor); //归还
|
predictorPool.returnObject(predictor); //归还
|
||||||
log.info("释放资源");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("归还Predictor失败", e);
|
log.warn("归还Predictor失败", e);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -220,7 +220,6 @@ public class RetinaFaceModel extends AbstractFaceModel implements AutoCloseable{
|
|||||||
if (predictor != null) {
|
if (predictor != null) {
|
||||||
try {
|
try {
|
||||||
predictorPool.returnObject(predictor); //归还
|
predictorPool.returnObject(predictor); //归还
|
||||||
log.info("释放资源");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("归还Predictor失败", e);
|
log.warn("归还Predictor失败", e);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
package cn.smartjavaai.face.model;
|
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.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.FileUtils;
|
||||||
import cn.smartjavaai.common.utils.ImageUtils;
|
import cn.smartjavaai.common.utils.ImageUtils;
|
||||||
import cn.smartjavaai.face.AbstractFaceModel;
|
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.entity.FaceResult;
|
||||||
import cn.smartjavaai.face.exception.FaceException;
|
import cn.smartjavaai.face.exception.FaceException;
|
||||||
import cn.smartjavaai.face.utils.FaceUtils;
|
import cn.smartjavaai.face.utils.FaceUtils;
|
||||||
|
import com.seeta.pool.*;
|
||||||
|
import com.seeta.sdk.*;
|
||||||
import com.seetaface.NativeLoader;
|
import com.seetaface.NativeLoader;
|
||||||
import com.seetaface.SeetaFace6JNI;
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.pool2.ObjectPool;
|
||||||
|
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
@@ -38,40 +45,76 @@ public class SeetaFace6Model extends AbstractFaceModel {
|
|||||||
|
|
||||||
private FaceModelConfig config;
|
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
|
@Override
|
||||||
public void loadModel(FaceModelConfig config) {
|
public void loadModel(FaceModelConfig config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
if (NativeLoader.seetaFace6SDK == null) {
|
if(StringUtils.isBlank(config.getModelPath())){
|
||||||
synchronized (lock) {
|
throw new FaceException("modelPath is null");
|
||||||
if(StringUtils.isBlank(config.getModelPath())){
|
}
|
||||||
throw new FaceException("modelPath is null");
|
//设置默认相似度阈值
|
||||||
}
|
if(config.getSimilarityThreshold() <= 0){
|
||||||
//加载依赖库
|
config.setSimilarityThreshold(SEETAFACE_DEFAULT_SIMILARITY_THRESHOLD);
|
||||||
NativeLoader.loadNativeLibraries(config.getModelPath());
|
}
|
||||||
log.info("Loading seetaFace6 library successfully.");
|
//加载依赖库
|
||||||
NativeLoader.seetaFace6SDK = new SeetaFace6JNI();
|
NativeLoader.loadNativeLibraries(config);
|
||||||
//加载模型
|
log.info("Loading seetaFace6 library successfully.");
|
||||||
boolean isSuccess = NativeLoader.seetaFace6SDK.initModel(config.getModelPath());
|
String[] faceDetectorModelPath = {config.getModelPath() + File.separator + "face_detector.csta"};
|
||||||
if(!isSuccess){
|
String[] faceRecognizerModelPath = {config.getModelPath() + File.separator + "face_recognizer.csta"};
|
||||||
throw new FaceException("seetaFace6模型初始化失败," + config.getModelPath());
|
String[] faceLandmarkerModelPath = {config.getModelPath() + File.separator + "face_landmarker_pts5.csta"};
|
||||||
}
|
SeetaDevice device = SeetaDevice.SEETA_DEVICE_AUTO;
|
||||||
log.info("Load seetaFace6 model success!");
|
int gpuId = 0;
|
||||||
new Thread(new Runnable() {
|
if(Objects.nonNull(config.getDevice())){
|
||||||
public void run() {
|
device = config.getDevice() == DeviceEnum.CPU ? SeetaDevice.SEETA_DEVICE_CPU : SeetaDevice.SEETA_DEVICE_GPU;
|
||||||
try {
|
if(config.getGpuId() >= 0 && device == SeetaDevice.SEETA_DEVICE_GPU){
|
||||||
log.info("start load faceDb...");
|
gpuId = config.getGpuId();
|
||||||
loadFaceDb();
|
|
||||||
log.info("Load faceDb success!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
@Override
|
||||||
@@ -110,9 +153,21 @@ public class SeetaFace6Model extends AbstractFaceModel {
|
|||||||
}
|
}
|
||||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||||
synchronized (lock) {
|
FaceDetector predictor = null;
|
||||||
SeetaRect[] seetaResult = NativeLoader.seetaFace6SDK.detect(imageData);
|
try {
|
||||||
|
predictor = faceDetectorPool.borrowObject();
|
||||||
|
SeetaRect[] seetaResult = predictor.Detect(imageData);
|
||||||
return FaceUtils.convertToDetectionResponse(seetaResult, config);
|
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
|
@Override
|
||||||
public float[] featureExtraction(BufferedImage image) {
|
public float[] featureExtraction(BufferedImage image) {
|
||||||
if(!ImageUtils.isImageValid(image)){
|
if(!ImageUtils.isImageValid(image)){
|
||||||
@@ -177,10 +302,56 @@ public class SeetaFace6Model extends AbstractFaceModel {
|
|||||||
}
|
}
|
||||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||||
synchronized (lock) {
|
FaceDetector faceDetector = null;
|
||||||
return NativeLoader.seetaFace6SDK.extractMaxFace(imageData);
|
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
|
@Override
|
||||||
@@ -228,8 +399,20 @@ public class SeetaFace6Model extends AbstractFaceModel {
|
|||||||
if(Objects.isNull(feature1) || Objects.isNull(feature2)){
|
if(Objects.isNull(feature1) || Objects.isNull(feature2)){
|
||||||
throw new FaceException("特征向量无效");
|
throw new FaceException("特征向量无效");
|
||||||
}
|
}
|
||||||
synchronized (lock) {
|
FaceRecognizer faceRecognizer = null;
|
||||||
return NativeLoader.seetaFace6SDK.calculateSimilarity(feature1, feature2);
|
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);
|
SeetaImageData imageData2 = new SeetaImageData(image2.getWidth(), image2.getHeight(), 3);
|
||||||
imageData2.data = ImageUtils.getMatrixBGR(image2);
|
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);
|
FaceRecognizer faceRecognizer = null;
|
||||||
SeetaImageData cropImageData2 = new SeetaImageData(cropImage2.getWidth(), cropImage2.getHeight(), 3);
|
FaceDatabase faceDatabase = null;
|
||||||
cropImageData2.data = ImageUtils.getMatrixBGR(cropImage2);
|
FaceLandmarker faceLandmarker = null;
|
||||||
return NativeLoader.seetaFace6SDK.compare(cropImageData1, cropImageData2);
|
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
|
@Override
|
||||||
public float featureComparison(byte[] imageData1, byte[] imageData2) {
|
public float featureComparison(byte[] imageData1, byte[] imageData2) {
|
||||||
if(Objects.isNull(imageData1) || Objects.isNull(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);
|
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||||
synchronized (lock) {
|
FaceDatabase faceDatabase = null;
|
||||||
byte[][] bytes = NativeLoader.seetaFace6SDK.crop(imageData);
|
try {
|
||||||
if (bytes == null || bytes.length == 0) {
|
faceDatabase = faceDatabasePool.borrowObject();
|
||||||
log.info("register face fail: key={}, error=no valid face", key);
|
SeetaImageData cropImageData = getMaxCropFace(imageData);
|
||||||
return false;
|
long index = faceDatabase.RegisterByCroppedFace(cropImageData);
|
||||||
}
|
|
||||||
long index = NativeLoader.seetaFace6SDK.registerCroppedFace(bytes[0]);
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
log.info("register face fail: key={}, index={}", key, index);
|
log.info("register face fail: key={}, index={}", key, index);
|
||||||
return false;
|
return false;
|
||||||
@@ -353,13 +592,25 @@ public class SeetaFace6Model extends AbstractFaceModel {
|
|||||||
FaceData face = new FaceData();
|
FaceData face = new FaceData();
|
||||||
face.setKey(key);
|
face.setKey(key);
|
||||||
face.setIndex(index);
|
face.setIndex(index);
|
||||||
face.setImgData(bytes[0]);
|
face.setImgData(cropImageData.data);
|
||||||
try {
|
try {
|
||||||
new FaceDao(config.getFaceDbPath()).save(face);
|
new FaceDao(config.getFaceDbPath()).save(face);
|
||||||
} catch (SQLException | ClassNotFoundException e) {
|
} catch (SQLException | ClassNotFoundException e) {
|
||||||
throw new FaceException("保存人脸库失败", e);
|
throw new FaceException("保存人脸库失败", e);
|
||||||
}
|
}
|
||||||
return true;
|
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
|
* @param faceData
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private boolean register(String key, FaceData faceData) {
|
private boolean registerCroppedFace(String key, FaceData faceData) {
|
||||||
synchronized (lock) {
|
FaceDatabase faceDatabase = null;
|
||||||
long index = NativeLoader.seetaFace6SDK.registerCroppedFace(faceData.getImgData());
|
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) {
|
if (index < 0) {
|
||||||
log.info("register face fail: key={}, index={}", key, index);
|
log.info("register face fail: key={}, index={}", key, index);
|
||||||
return false;
|
return false;
|
||||||
@@ -414,6 +669,18 @@ public class SeetaFace6Model extends AbstractFaceModel {
|
|||||||
throw new FaceException(e);
|
throw new FaceException(e);
|
||||||
}
|
}
|
||||||
return rows > 0;
|
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);
|
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||||
synchronized (lock) {
|
FaceDatabase faceDatabase = null;
|
||||||
RecognizeResult recognizeResult = NativeLoader.seetaFace6SDK.query(imageData);
|
try {
|
||||||
return searchFaceDb(recognizeResult);
|
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()){
|
if(!checkFaceDb()){
|
||||||
throw new FaceException("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
|
throw new FaceException("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
|
||||||
}
|
}
|
||||||
synchronized (lock) {
|
|
||||||
try {
|
FaceDatabase faceDatabase = null;
|
||||||
List<Long> list = new FaceDao(config.getFaceDbPath()).findIndexList(keys);
|
try {
|
||||||
if (list == null) {
|
List<Long> list = new FaceDao(config.getFaceDbPath()).findIndexList(keys);
|
||||||
return 0;
|
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
|
@Override
|
||||||
@@ -507,16 +809,31 @@ public class SeetaFace6Model extends AbstractFaceModel {
|
|||||||
if(!checkFaceDb()){
|
if(!checkFaceDb()){
|
||||||
throw new FaceException("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
|
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 {
|
try {
|
||||||
new FaceDao(config.getFaceDbPath()).deleteAll();
|
rows = new FaceDao(config.getFaceDbPath()).deleteAll();
|
||||||
} catch (SQLException | ClassNotFoundException e) {
|
} catch (SQLException | ClassNotFoundException e) {
|
||||||
throw new FaceException("删除人脸库失败", e);
|
throw new FaceException("删除人脸库失败", e);
|
||||||
}
|
}
|
||||||
return rows;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FaceResult searchFaceDb(RecognizeResult recognizeResult) {
|
private FaceResult searchFaceDb(long index,float similar) {
|
||||||
if(recognizeResult != null && recognizeResult.index >= 0){
|
if(index >= 0){
|
||||||
String key = null;
|
String key = null;
|
||||||
synchronized (lock) {
|
try {
|
||||||
try {
|
key = new FaceDao(config.getFaceDbPath()).findKeyByIndex(index);
|
||||||
key = new FaceDao(config.getFaceDbPath()).findKeyByIndex(recognizeResult.index);
|
} catch (SQLException | ClassNotFoundException e) {
|
||||||
} catch (SQLException | ClassNotFoundException e) {
|
throw new FaceException("查询人脸库失败", e);
|
||||||
throw new FaceException("查询人脸库失败", e);
|
|
||||||
}
|
|
||||||
return new FaceResult(key, recognizeResult.similar);
|
|
||||||
}
|
}
|
||||||
|
return new FaceResult(key, similar);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -571,7 +886,7 @@ public class SeetaFace6Model extends AbstractFaceModel {
|
|||||||
}
|
}
|
||||||
list.forEach(face -> {
|
list.forEach(face -> {
|
||||||
try {
|
try {
|
||||||
register(face.getKey(), face);
|
registerCroppedFace(face.getKey(), face);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,7 +209,6 @@ public class UltraLightFastGenericFaceModel extends AbstractFaceModel implements
|
|||||||
if (predictor != null) {
|
if (predictor != null) {
|
||||||
try {
|
try {
|
||||||
predictorPool.returnObject(predictor); //归还
|
predictorPool.returnObject(predictor); //归还
|
||||||
log.info("释放资源");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("归还Predictor失败", e);
|
log.warn("归还Predictor失败", e);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import cn.smartjavaai.common.entity.DetectionRectangle;
|
|||||||
import cn.smartjavaai.common.utils.ImageUtils;
|
import cn.smartjavaai.common.utils.ImageUtils;
|
||||||
import cn.smartjavaai.face.FaceModelConfig;
|
import cn.smartjavaai.face.FaceModelConfig;
|
||||||
import cn.smartjavaai.face.exception.FaceException;
|
import cn.smartjavaai.face.exception.FaceException;
|
||||||
import com.seetaface.model.SeetaRect;
|
import com.seeta.sdk.SeetaRect;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -74,10 +74,10 @@ public class FaceUtils {
|
|||||||
List<DetectionRectangle> rectangleList = new ArrayList<DetectionRectangle>();
|
List<DetectionRectangle> rectangleList = new ArrayList<DetectionRectangle>();
|
||||||
for(SeetaRect rect : seetaResult){
|
for(SeetaRect rect : seetaResult){
|
||||||
//过滤置信度
|
//过滤置信度
|
||||||
if(config.getConfidenceThreshold() > 0 && rect.score < config.getConfidenceThreshold()){
|
/*if(config.getConfidenceThreshold() > 0){
|
||||||
continue;
|
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);
|
rectangleList.add(rectangle);
|
||||||
}
|
}
|
||||||
detectionResponse.setRectangleList(rectangleList);
|
detectionResponse.setRectangleList(rectangleList);
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
package com.seetaface;
|
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 jdk.dynalink.linker.support.Lookup;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -12,7 +23,10 @@ import java.lang.reflect.Field;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardCopyOption;
|
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 {
|
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.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[] 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_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[] 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 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 {
|
try {
|
||||||
// 创建临时目录
|
OsInfo osInfo = SystemUtil.getOsInfo();
|
||||||
tempNativeDir = Files.createTempDirectory(TEMP_DIR);
|
//检查当前系统是否支持
|
||||||
log.info("create temp native directory: " + tempNativeDir.toAbsolutePath().toString());
|
if(!osInfo.isWindows() && !osInfo.isLinux()){
|
||||||
|
throw new FaceException("当前系统不支持:" + osInfo.getName());
|
||||||
// 获取当前平台库列表
|
|
||||||
String libDir = getLibDir();
|
|
||||||
String[] libNames = getPlatformLibs(libDir);
|
|
||||||
|
|
||||||
// 批量提取库文件
|
|
||||||
for (String libName : libNames) {
|
|
||||||
extractLibrary(libName,libDir);
|
|
||||||
}
|
}
|
||||||
|
//判断硬件架构是否支持GPU
|
||||||
String separator = System.getProperty("path.separator");
|
if(config.getDevice() != null && config.getDevice().equals(DeviceEnum.GPU)){
|
||||||
String sysLib = System.getProperty("java.library.path");
|
//GPU仅支持amd64
|
||||||
if (sysLib.endsWith(separator)) {
|
if(!osInfo.getArch().contains("amd64") && !osInfo.getArch().contains("x86_64")){
|
||||||
System.setProperty("java.library.path", sysLib + tempNativeDir);
|
throw new FaceException("seetaface6 GPU模型不支持当前arch:" + osInfo.getArch());
|
||||||
} else {
|
}
|
||||||
System.setProperty("java.library.path", sysLib + separator + tempNativeDir);
|
|
||||||
}
|
}
|
||||||
|
seetaface6NativePath = Paths.get(Config.getCachePath(), SEETAFACE_LIB_DIR);
|
||||||
|
//创建目录
|
||||||
// 按顺序加载库(确保依赖关系)
|
FileUtil.mkdir(seetaface6NativePath);
|
||||||
for (String libName : libNames) {
|
log.info("seetaface6依赖库路径: " + seetaface6NativePath.toAbsolutePath().toString());
|
||||||
log.info("Loading library: " + tempNativeDir + File.separator + libName);
|
//拷贝依赖库到缓存目录
|
||||||
System.load(tempNativeDir + File.separator + libName);
|
List<File> 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) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Native library loading failed", e);
|
throw new RuntimeException("Native library loading failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
private static String[] getPlatformLibs(String libDir) {
|
* 拷贝依赖库到缓存目录
|
||||||
if (libDir.contains("windows")) return WIN_LIBS;
|
* @param osInfo
|
||||||
if (libDir.contains("centos")) return LINUX_CENTOS_LIBS;
|
* @return
|
||||||
if (libDir.contains("ubuntu")) return LINUX_UBUNTU_LIBS;
|
*/
|
||||||
throw new UnsupportedOperationException("Unsupported OS");
|
private static List<File> getLibFiles(OsInfo osInfo,DeviceEnum deviceEnum){
|
||||||
|
try {
|
||||||
|
String device = getDevice(deviceEnum);
|
||||||
|
log.info("当前设备:{}", device);
|
||||||
|
//获取dll文件列表
|
||||||
|
List<DllItem> baseList = new ArrayList<>();
|
||||||
|
List<DllItem> 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<String> basePath = getSortedPath(baseList);
|
||||||
|
List<String> sdkPath = getSortedPath(jniList);
|
||||||
|
List<File> 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 libPath
|
||||||
* @param libDir
|
* @return
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private static void extractLibrary(String libName,String libDir) throws IOException {
|
private static File extractLibrary(String libPath) throws IOException {
|
||||||
String resourcePath = "/native" + libDir + "/" + libName;
|
String resourcePath = libPath;
|
||||||
try (InputStream in = NativeLoader.class.getResourceAsStream(resourcePath)) {
|
try (InputStream in = com.seetaface.NativeLoader.class.getResourceAsStream(resourcePath)) {
|
||||||
if (in == null) throw new FileNotFoundException(resourcePath);
|
if (in == null) throw new FileNotFoundException(resourcePath);
|
||||||
|
Path path = Paths.get(resourcePath);
|
||||||
Path targetPath = tempNativeDir.resolve(libName);
|
String fileName = path.getFileName().toString();
|
||||||
|
Path targetPath = seetaface6NativePath.resolve(fileName);
|
||||||
Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
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);
|
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<String>
|
||||||
*/
|
*/
|
||||||
private static String getLinuxOsName(){
|
private static List<String> getSortedPath(List<DllItem> list) {
|
||||||
try (BufferedReader reader = new BufferedReader(new FileReader("/etc/os-release"))) {
|
return list.stream().sorted(Comparator.comparing(dllItem -> {
|
||||||
String line;
|
int i = dllItem.getKey().lastIndexOf(".") + 1;
|
||||||
while ((line = reader.readLine()) != null) {
|
String substring = dllItem.getKey().substring(i);
|
||||||
if (line.startsWith("ID=")) {
|
return Integer.valueOf(substring);
|
||||||
String distro = line.substring(3).replace("\"", "").trim();
|
})).map(DllItem::getValue).collect(Collectors.toList());
|
||||||
return distro;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.out.println("Failed to read /etc/os-release: " + e.getMessage());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>ink.numberone</groupId>
|
<groupId>ink.numberone</groupId>
|
||||||
<artifactId>smartjavaai-parent</artifactId>
|
<artifactId>smartjavaai-parent</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>smartjavaai-objectdetection</artifactId>
|
<artifactId>smartjavaai-objectdetection</artifactId>
|
||||||
<version>1.0.8</version>
|
<version>1.0.10</version>
|
||||||
<name>smartjavaai-objectdetection</name>
|
<name>smartjavaai-objectdetection</name>
|
||||||
<description>SmartJavaAI</description>
|
<description>SmartJavaAI</description>
|
||||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class DetectorModel implements AutoCloseable{
|
|||||||
/**
|
/**
|
||||||
* 目标检测-将检测结果绘制到原图
|
* 目标检测-将检测结果绘制到原图
|
||||||
* @param imagePath
|
* @param imagePath
|
||||||
* @return
|
* @param outputPath
|
||||||
*/
|
*/
|
||||||
public void detectAndDraw(String imagePath, String outputPath){
|
public void detectAndDraw(String imagePath, String outputPath){
|
||||||
if(!FileUtils.isFileExists(imagePath)){
|
if(!FileUtils.isFileExists(imagePath)){
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package cn.smartjavaai.objectdetection.model;
|
package cn.smartjavaai.objectdetection.model;
|
||||||
|
|
||||||
|
import cn.smartjavaai.common.config.Config;
|
||||||
import cn.smartjavaai.objectdetection.DetectorModelConfig;
|
import cn.smartjavaai.objectdetection.DetectorModelConfig;
|
||||||
import cn.smartjavaai.objectdetection.DetectorModelEnum;
|
import cn.smartjavaai.objectdetection.DetectorModelEnum;
|
||||||
import cn.smartjavaai.objectdetection.exception.DetectionException;
|
import cn.smartjavaai.objectdetection.exception.DetectionException;
|
||||||
@@ -21,6 +22,10 @@ public class ObjectDetectionModelFactory {
|
|||||||
|
|
||||||
private static final ConcurrentHashMap<String, DetectorModel> modelMap = new ConcurrentHashMap<>();
|
private static final ConcurrentHashMap<String, DetectorModel> modelMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
static{
|
||||||
|
log.info("缓存目录:{}", Config.getCachePath());
|
||||||
|
}
|
||||||
|
|
||||||
// 私有构造函数,防止外部创建实例
|
// 私有构造函数,防止外部创建实例
|
||||||
private ObjectDetectionModelFactory() {}
|
private ObjectDetectionModelFactory() {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>ink.numberone</groupId>
|
|
||||||
<artifactId>smartjavaai-parent</artifactId>
|
|
||||||
<version>1.0.8</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>smartjavaai-seetaface6-lib</artifactId>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source>11</maven.compiler.source>
|
|
||||||
<maven.compiler.target>11</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<description>SmartJavaAI</description>
|
|
||||||
<url>https://github.com/geekwenjie/SmartJavaAI</url>
|
|
||||||
<licenses>
|
|
||||||
<license>
|
|
||||||
<name>MIT License</name>
|
|
||||||
<url>https://opensource.org/licenses/MIT</url>
|
|
||||||
</license>
|
|
||||||
</licenses>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.sonatype.central</groupId>
|
|
||||||
<artifactId>central-publishing-maven-plugin</artifactId>
|
|
||||||
<version>0.4.0</version>
|
|
||||||
<extensions>true</extensions>
|
|
||||||
<configuration>
|
|
||||||
<publishingServerId>dengwenjie</publishingServerId>
|
|
||||||
<tokenAuth>true</tokenAuth>
|
|
||||||
<deploymentName>${project.groupId}:${project.artifactId}:${project.version}</deploymentName>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
|
||||||
<version>3.1.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>attach-sources</id>
|
|
||||||
<goals>
|
|
||||||
<goal>jar-no-fork</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
|
||||||
<version>3.1.0</version>
|
|
||||||
<configuration>
|
|
||||||
<javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
|
|
||||||
<doclint>none</doclint>
|
|
||||||
<additionalJOptions>
|
|
||||||
<additionalJOption>-Xdoclint:none</additionalJOption>
|
|
||||||
</additionalJOptions>
|
|
||||||
</configuration>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>attach-javadocs</id>
|
|
||||||
<goals>
|
|
||||||
<goal>jar</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-gpg-plugin</artifactId>
|
|
||||||
<version>3.1.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>sign-artifacts</id>
|
|
||||||
<phase>verify</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>sign</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<!-- 必须添加:SCM信息 -->
|
|
||||||
<scm>
|
|
||||||
<connection>scm:git:git://github.com/geekwenjie/SmartJavaAI.git</connection>
|
|
||||||
<developerConnection>scm:git:ssh://github.com/geekwenjie/SmartJavaAI.git</developerConnection>
|
|
||||||
<url>http://github.com/geekwenjie/SmartJavaAI/tree/master</url>
|
|
||||||
</scm>
|
|
||||||
|
|
||||||
|
|
||||||
<distributionManagement>
|
|
||||||
<snapshotRepository>
|
|
||||||
<id>dengwenjie</id>
|
|
||||||
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
|
|
||||||
</snapshotRepository>
|
|
||||||
<repository>
|
|
||||||
<id>dengwenjie</id>
|
|
||||||
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
|
||||||
</repository>
|
|
||||||
</distributionManagement>
|
|
||||||
|
|
||||||
<developers>
|
|
||||||
<developer>
|
|
||||||
<name>dengwenjie</name>
|
|
||||||
<email>775747758@qq.com</email>
|
|
||||||
<roles>
|
|
||||||
<role>Project Manager</role>
|
|
||||||
<role>Architect</role>
|
|
||||||
</roles>
|
|
||||||
</developer>
|
|
||||||
</developers>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user