mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-14 13:48:56 +00:00
1、seetaface6兼容linux系统
2、支持设置全局缓存路径 3、优化部分功能
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<Long> list = new FaceDao(config.getFaceDbPath()).findIndexList(keys);
|
||||
if (list == null) {
|
||||
return 0;
|
||||
|
||||
FaceDatabase faceDatabase = null;
|
||||
try {
|
||||
List<Long> 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();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<DetectionRectangle> rectangleList = new ArrayList<DetectionRectangle>();
|
||||
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);
|
||||
|
||||
@@ -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<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) {
|
||||
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<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 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<String>
|
||||
*/
|
||||
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<String> getSortedPath(List<DllItem> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user