1、seetaface6兼容linux系统

2、支持设置全局缓存路径
3、优化部分功能
This commit is contained in:
dengwenjie
2025-04-19 19:48:12 +08:00
parent 56f221662b
commit 526bc1c8d7
36 changed files with 736 additions and 324 deletions

View File

@@ -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
View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
}
}
}

View File

@@ -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();
}
} }

View File

@@ -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>

View File

@@ -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;
} }

View File

@@ -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;
} }

View File

@@ -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());
} }
} }

View File

@@ -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);
} }

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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();
} }

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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;
} }
} }

View File

@@ -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>

View File

@@ -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)){

View File

@@ -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() {}

View File

@@ -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>