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