mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-13 13:18:58 +00:00
1、集成车牌识别模型,支持车牌检测与识别
2、新增 Milvus 身份验证支持 3、目标检测功能升级:可指定类别及topk 4、支持自定义线程池线程数量
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.config.ModelConfig;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.enums.FaceAttributeModelEnum;
|
||||
import lombok.Data;
|
||||
@@ -9,7 +10,7 @@ import lombok.Data;
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class FaceAttributeConfig {
|
||||
public class FaceAttributeConfig extends ModelConfig {
|
||||
|
||||
/**
|
||||
* 人脸属性识别模型枚举
|
||||
@@ -21,16 +22,6 @@ public class FaceAttributeConfig {
|
||||
*/
|
||||
private String modelPath;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private DeviceEnum device;
|
||||
|
||||
/**
|
||||
* gpu设备ID 当device为GPU时生效
|
||||
*/
|
||||
private int gpuId = 0;
|
||||
|
||||
/**
|
||||
* 是否启用年龄检测
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.config.ModelConfig;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.constant.FaceDetectConstant;
|
||||
import cn.smartjavaai.face.enums.FaceDetModelEnum;
|
||||
@@ -13,7 +14,7 @@ import java.util.Map;
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class FaceDetConfig {
|
||||
public class FaceDetConfig extends ModelConfig {
|
||||
|
||||
/**
|
||||
* 人脸检测模型枚举
|
||||
@@ -36,16 +37,6 @@ public class FaceDetConfig {
|
||||
*/
|
||||
private String modelPath;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private DeviceEnum device;
|
||||
|
||||
/**
|
||||
* 个性化配置(按模型类型动态解析)
|
||||
*/
|
||||
private Map<String, Object> customParams = new HashMap<>();
|
||||
|
||||
|
||||
public FaceDetConfig() {
|
||||
}
|
||||
@@ -59,19 +50,4 @@ public class FaceDetConfig {
|
||||
this.modelPath = modelPath;
|
||||
}
|
||||
|
||||
public <T> T getCustomParam(String key, Class<T> clazz) {
|
||||
Object value = customParams.get(key);
|
||||
if (value == null) return null;
|
||||
return clazz.cast(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加个性化配置项
|
||||
*/
|
||||
public void putCustomParam(String key, Object value) {
|
||||
if (customParams == null) {
|
||||
customParams = new HashMap<>();
|
||||
}
|
||||
customParams.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.config.ModelConfig;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.enums.ExpressionModelEnum;
|
||||
import cn.smartjavaai.face.model.facedect.FaceDetModel;
|
||||
@@ -14,7 +15,7 @@ import java.util.Map;
|
||||
* @date 2025/7/1
|
||||
*/
|
||||
@Data
|
||||
public class FaceExpressionConfig {
|
||||
public class FaceExpressionConfig extends ModelConfig {
|
||||
|
||||
/**
|
||||
* 模型枚举
|
||||
@@ -26,11 +27,6 @@ public class FaceExpressionConfig {
|
||||
*/
|
||||
private String modelPath;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private DeviceEnum device;
|
||||
|
||||
/**
|
||||
* 人脸检测模型
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.config.ModelConfig;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.constant.FaceDetectConstant;
|
||||
import cn.smartjavaai.face.enums.FaceRecModelEnum;
|
||||
@@ -16,7 +17,7 @@ import java.util.Map;
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class FaceRecConfig {
|
||||
public class FaceRecConfig extends ModelConfig {
|
||||
|
||||
/**
|
||||
* 人脸模型枚举
|
||||
@@ -28,11 +29,6 @@ public class FaceRecConfig {
|
||||
*/
|
||||
private String modelPath;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private DeviceEnum device;
|
||||
|
||||
|
||||
/**
|
||||
* 向量数据库配置
|
||||
@@ -60,10 +56,6 @@ public class FaceRecConfig {
|
||||
*/
|
||||
private FaceDetModel detectModel;
|
||||
|
||||
/**
|
||||
* 个性化配置(按模型类型动态解析)
|
||||
*/
|
||||
private Map<String, Object> customParams = new HashMap<>();
|
||||
|
||||
public FaceRecConfig() {
|
||||
}
|
||||
@@ -77,19 +69,5 @@ public class FaceRecConfig {
|
||||
this.modelPath = modelPath;
|
||||
}
|
||||
|
||||
public <T> T getCustomParam(String key, Class<T> clazz) {
|
||||
Object value = customParams.get(key);
|
||||
if (value == null) return null;
|
||||
return clazz.cast(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加个性化配置项
|
||||
*/
|
||||
public void putCustomParam(String key, Object value) {
|
||||
if (customParams == null) {
|
||||
customParams = new HashMap<>();
|
||||
}
|
||||
customParams.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.config.ModelConfig;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.constant.LivenessConstant;
|
||||
import cn.smartjavaai.face.enums.LivenessModelEnum;
|
||||
@@ -15,7 +16,7 @@ import java.util.Map;
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class LivenessConfig {
|
||||
public class LivenessConfig extends ModelConfig {
|
||||
|
||||
/**
|
||||
* 活体检测模型枚举
|
||||
@@ -27,20 +28,12 @@ public class LivenessConfig {
|
||||
*/
|
||||
private String modelPath;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private DeviceEnum device;
|
||||
|
||||
/**
|
||||
* 人脸检测模型
|
||||
*/
|
||||
private FaceDetModel detectModel;
|
||||
|
||||
/**
|
||||
* 个性化配置(按模型类型动态解析)
|
||||
*/
|
||||
private Map<String, Object> customParams = new HashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
@@ -74,20 +67,4 @@ public class LivenessConfig {
|
||||
this.modelPath = modelPath;
|
||||
}
|
||||
|
||||
// 可选封装方法,便于类型转换和调用
|
||||
public <T> T getCustomParam(String key, Class<T> clazz) {
|
||||
Object value = customParams.get(key);
|
||||
if (value == null) return null;
|
||||
return clazz.cast(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加个性化配置项
|
||||
*/
|
||||
public void putCustomParam(String key, Object value) {
|
||||
if (customParams == null) {
|
||||
customParams = new HashMap<>();
|
||||
}
|
||||
customParams.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.config;
|
||||
|
||||
import cn.smartjavaai.common.config.ModelConfig;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.face.constant.LivenessConstant;
|
||||
import cn.smartjavaai.face.enums.LivenessModelEnum;
|
||||
@@ -11,7 +12,7 @@ import lombok.Data;
|
||||
* @author dwj
|
||||
*/
|
||||
@Data
|
||||
public class QualityConfig {
|
||||
public class QualityConfig extends ModelConfig {
|
||||
|
||||
/**
|
||||
* 活体检测模型枚举
|
||||
@@ -23,16 +24,6 @@ public class QualityConfig {
|
||||
*/
|
||||
private String modelPath;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private DeviceEnum device;
|
||||
|
||||
/**
|
||||
* gpu设备ID 当device为GPU时生效
|
||||
*/
|
||||
private int gpuId = 0;
|
||||
|
||||
|
||||
public QualityConfig() {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.model.attribute;
|
||||
|
||||
import ai.djl.engine.Engine;
|
||||
import cn.smartjavaai.common.entity.*;
|
||||
import cn.smartjavaai.common.entity.face.FaceAttribute;
|
||||
import cn.smartjavaai.common.entity.face.FaceInfo;
|
||||
@@ -102,6 +103,19 @@ public class Seetaface6FaceAttributeModel implements FaceAttributeModel {
|
||||
this.eyeStateDetectorPool = new EyeStateDetectorPool(eyeStateDetectorPoolConfSetting);
|
||||
this.maskDetectorPool = new MaskDetectorPool(maskDetectorPoolConfSetting);
|
||||
this.poseEstimatorPool = new PoseEstimatorPool(poseEstimatorPoolConfSetting);
|
||||
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
faceDetectorPool.setMaxTotal(predictorPoolSize);
|
||||
faceLandmarkerPool.setMaxTotal(predictorPoolSize);
|
||||
genderPredictorPool.setMaxTotal(predictorPoolSize);
|
||||
agePredictorPool.setMaxTotal(predictorPoolSize);
|
||||
eyeStateDetectorPool.setMaxTotal(predictorPoolSize);
|
||||
maskDetectorPool.setMaxTotal(predictorPoolSize);
|
||||
poseEstimatorPool.setMaxTotal(predictorPoolSize);
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new FaceException(e);
|
||||
}
|
||||
@@ -471,6 +485,35 @@ public class Seetaface6FaceAttributeModel implements FaceAttributeModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public FaceDetectorPool getFaceDetectorPool() {
|
||||
return faceDetectorPool;
|
||||
}
|
||||
|
||||
public GenderPredictorPool getGenderPredictorPool() {
|
||||
return genderPredictorPool;
|
||||
}
|
||||
|
||||
public FaceLandmarkerPool getFaceLandmarkerPool() {
|
||||
return faceLandmarkerPool;
|
||||
}
|
||||
|
||||
public AgePredictorPool getAgePredictorPool() {
|
||||
return agePredictorPool;
|
||||
}
|
||||
|
||||
public EyeStateDetectorPool getEyeStateDetectorPool() {
|
||||
return eyeStateDetectorPool;
|
||||
}
|
||||
|
||||
public MaskDetectorPool getMaskDetectorPool() {
|
||||
return maskDetectorPool;
|
||||
}
|
||||
|
||||
public PoseEstimatorPool getPoseEstimatorPool() {
|
||||
return poseEstimatorPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if(Objects.nonNull(faceDetectorPool)){
|
||||
|
||||
@@ -2,6 +2,7 @@ package cn.smartjavaai.face.model.expression;
|
||||
|
||||
import ai.djl.Device;
|
||||
import ai.djl.MalformedModelException;
|
||||
import ai.djl.engine.Engine;
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.Classifications;
|
||||
import ai.djl.modality.cv.Image;
|
||||
@@ -32,6 +33,7 @@ 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 org.opencv.core.Mat;
|
||||
import org.opencv.face.Face;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
@@ -56,7 +58,7 @@ public class CommonEmotionModel implements ExpressionModel{
|
||||
|
||||
private ZooModel<Image, Classifications> model;
|
||||
|
||||
private ObjectPool<Predictor<Image, Classifications>> predictorPool;
|
||||
private GenericObjectPool<Predictor<Image, Classifications>> predictorPool;
|
||||
|
||||
@Override
|
||||
public void loadModel(FaceExpressionConfig config) {
|
||||
@@ -73,6 +75,14 @@ public class CommonEmotionModel implements ExpressionModel{
|
||||
try {
|
||||
model = criteria.loadModel();
|
||||
this.predictorPool = new GenericObjectPool<>(new PredictorFactory<>(model));
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
predictorPool.setMaxTotal(predictorPoolSize);
|
||||
log.debug("当前设备: " + model.getNDManager().getDevice());
|
||||
log.debug("当前引擎: " + Engine.getInstance().getEngineName());
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
} catch (IOException | ModelNotFoundException | MalformedModelException e) {
|
||||
throw new FaceException("DenseNetEmotionModel模型加载失败", e);
|
||||
}
|
||||
@@ -153,6 +163,7 @@ public class CommonEmotionModel implements ExpressionModel{
|
||||
result.setClassifications(classifications);
|
||||
faceInfo.setExpressionResult(result);
|
||||
}
|
||||
((Mat)djlImage.getWrappedImage()).release();
|
||||
return faceDetectionResponse;
|
||||
}
|
||||
|
||||
@@ -226,6 +237,7 @@ public class CommonEmotionModel implements ExpressionModel{
|
||||
result.setClassifications(classifications);
|
||||
expressionResults.add(result);
|
||||
}
|
||||
((Mat)djlImage.getWrappedImage()).release();
|
||||
return R.ok(expressionResults);
|
||||
}
|
||||
|
||||
@@ -276,6 +288,7 @@ public class CommonEmotionModel implements ExpressionModel{
|
||||
FacialExpression expression = FacialExpression.fromLabel(bestClass.getClassName());
|
||||
ExpressionResult result = new ExpressionResult(expression, (float)bestClass.getProbability());
|
||||
result.setClassifications(classifications);
|
||||
((Mat)djlImage.getWrappedImage()).release();
|
||||
return R.ok(result);
|
||||
|
||||
}
|
||||
@@ -342,7 +355,10 @@ public class CommonEmotionModel implements ExpressionModel{
|
||||
return detectTopFace(imageData);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public GenericObjectPool<Predictor<Image, Classifications>> getPool() {
|
||||
return predictorPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package cn.smartjavaai.face.model.expression;
|
||||
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.Classifications;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.output.DetectedObjects;
|
||||
import cn.smartjavaai.common.entity.DetectionRectangle;
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.entity.Point;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.common.entity.face.ExpressionResult;
|
||||
import cn.smartjavaai.face.config.FaceExpressionConfig;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
@@ -190,6 +195,9 @@ public interface ExpressionModel extends AutoCloseable{
|
||||
|
||||
|
||||
|
||||
default GenericObjectPool<Predictor<Image, Classifications>> getPool(){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class EmotionCriteriaFactory {
|
||||
public static Criteria<Image, Classifications> createCriteria(FaceExpressionConfig config) {
|
||||
Device device = null;
|
||||
if(!Objects.isNull(config.getDevice())){
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu();
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu(config.getGpuId());
|
||||
}
|
||||
Criteria<Image, Classifications> criteria = null;
|
||||
if(config.getModelEnum() == ExpressionModelEnum.DensNet121){
|
||||
@@ -50,6 +50,7 @@ public class EmotionCriteriaFactory {
|
||||
.optModelPath(Paths.get(config.getModelPath()))
|
||||
.optTranslator(new FrEmotionTranslator(224))
|
||||
.optProgress(new ProgressBar())
|
||||
.optDevice(device)
|
||||
.build();
|
||||
}
|
||||
return criteria;
|
||||
|
||||
@@ -24,6 +24,7 @@ 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 org.opencv.core.Mat;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -43,7 +44,7 @@ import java.util.Objects;
|
||||
@Slf4j
|
||||
public class CommonFaceDetModel implements FaceDetModel{
|
||||
|
||||
private ObjectPool<Predictor<Image, DetectedObjects>> predictorPool;
|
||||
private GenericObjectPool<Predictor<Image, DetectedObjects>> predictorPool;
|
||||
|
||||
private ZooModel<Image, DetectedObjects> model;
|
||||
|
||||
@@ -58,8 +59,14 @@ public class CommonFaceDetModel implements FaceDetModel{
|
||||
try {
|
||||
model = criteria.loadModel();
|
||||
this.predictorPool = new GenericObjectPool<>(new PredictorFactory<>(model));
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
predictorPool.setMaxTotal(predictorPoolSize);
|
||||
log.debug("当前设备: " + model.getNDManager().getDevice());
|
||||
log.debug("当前引擎: " + Engine.getInstance().getEngineName());
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
} catch (IOException | ModelNotFoundException | MalformedModelException e) {
|
||||
throw new FaceException("人脸检测模型加载失败", e);
|
||||
}
|
||||
@@ -81,11 +88,16 @@ public class CommonFaceDetModel implements FaceDetModel{
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
DetectedObjects detection = detect(img);
|
||||
return R.ok(FaceUtils.convertToDetectionResponse(detection,img));
|
||||
} catch (IOException e) {
|
||||
throw new FaceException("无效的图片", e);
|
||||
} finally {
|
||||
if (img != null) {
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
}
|
||||
DetectedObjects detection = detect(img);
|
||||
return R.ok(FaceUtils.convertToDetectionResponse(detection,img));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,12 +111,17 @@ public class CommonFaceDetModel implements FaceDetModel{
|
||||
if(Objects.isNull(imageInputStream)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
Image img = ImageFactory.getInstance().fromInputStream(imageInputStream);
|
||||
img = ImageFactory.getInstance().fromInputStream(imageInputStream);
|
||||
DetectedObjects detection = detect(img);
|
||||
return R.ok(FaceUtils.convertToDetectionResponse(detection,img));
|
||||
} catch (IOException e) {
|
||||
throw new FaceException("无效图片输入流", e);
|
||||
} finally {
|
||||
if (img != null) {
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -114,9 +131,19 @@ public class CommonFaceDetModel implements FaceDetModel{
|
||||
if(!ImageUtils.isImageValid(image)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
|
||||
DetectedObjects detection = detect(img);
|
||||
return R.ok(FaceUtils.convertToDetectionResponse(detection,img));
|
||||
Image img = null;
|
||||
try {
|
||||
img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
|
||||
DetectedObjects detection = detect(img);
|
||||
return R.ok(FaceUtils.convertToDetectionResponse(detection,img));
|
||||
} catch (Exception e) {
|
||||
throw new FaceException(e);
|
||||
} finally {
|
||||
if (img != null) {
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,11 +151,7 @@ public class CommonFaceDetModel implements FaceDetModel{
|
||||
if(Objects.isNull(imageData)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
try {
|
||||
return detect(ImageIO.read(new ByteArrayInputStream(imageData)));
|
||||
} catch (IOException e) {
|
||||
throw new FaceException("错误的图像", e);
|
||||
}
|
||||
return detect(new ByteArrayInputStream(imageData));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,8 +168,9 @@ public class CommonFaceDetModel implements FaceDetModel{
|
||||
if(!FileUtils.isFileExists(imagePath)){
|
||||
return R.fail(R.Status.FILE_NOT_FOUND);
|
||||
}
|
||||
Image img = null;
|
||||
try {
|
||||
Image img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
|
||||
DetectedObjects detectedObjects = detect(img);
|
||||
if(Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
|
||||
return R.fail(R.Status.NO_FACE_DETECTED);
|
||||
@@ -158,6 +182,10 @@ public class CommonFaceDetModel implements FaceDetModel{
|
||||
return R.ok();
|
||||
} catch (IOException e) {
|
||||
throw new FaceException(e);
|
||||
} finally {
|
||||
if (img != null){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +209,10 @@ public class CommonFaceDetModel implements FaceDetModel{
|
||||
return R.ok(ImageIO.read(new ByteArrayInputStream(imageBytes)));
|
||||
} catch (IOException e) {
|
||||
throw new FaceException("导出图片失败", e);
|
||||
} finally {
|
||||
if (img != null){
|
||||
((Mat)img.getWrappedImage()).release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +245,11 @@ public class CommonFaceDetModel implements FaceDetModel{
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public GenericObjectPool<Predictor<Image, DetectedObjects>> getPool() {
|
||||
return predictorPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package cn.smartjavaai.face.model.facedect;
|
||||
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.Classifications;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.output.DetectedObjects;
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.face.config.FaceDetConfig;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.InputStream;
|
||||
@@ -83,6 +88,10 @@ public interface FaceDetModel extends AutoCloseable{
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
default GenericObjectPool<Predictor<Image, DetectedObjects>> getPool(){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.model.facedect;
|
||||
|
||||
import ai.djl.engine.Engine;
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
@@ -48,13 +49,9 @@ public class SeetaFace6FaceDetModel implements FaceDetModel{
|
||||
String[] faceDetectorModelPath = {config.getModelPath() + File.separator + "face_detector.csta"};
|
||||
String[] faceLandmarkerModelPath = {config.getModelPath() + File.separator + "face_landmarker_pts5.csta"};
|
||||
SeetaDevice device = SeetaDevice.SEETA_DEVICE_AUTO;
|
||||
int gpuId = 0;
|
||||
int gpuId = config.getGpuId();
|
||||
if(Objects.nonNull(config.getDevice())){
|
||||
device = config.getDevice() == DeviceEnum.CPU ? SeetaDevice.SEETA_DEVICE_CPU : SeetaDevice.SEETA_DEVICE_GPU;
|
||||
Integer gpuIdValue = config.getCustomParam("gpuId", Integer.class);
|
||||
if(Objects.nonNull(gpuIdValue) && device == SeetaDevice.SEETA_DEVICE_GPU){
|
||||
gpuId = gpuIdValue;
|
||||
}
|
||||
}
|
||||
try {
|
||||
SeetaModelSetting faceDetectorPoolSetting = new SeetaModelSetting(gpuId, faceDetectorModelPath, device);
|
||||
@@ -65,6 +62,14 @@ public class SeetaFace6FaceDetModel implements FaceDetModel{
|
||||
|
||||
this.faceDetectorPool = new FaceDetectorPool(faceDetectorPoolConfSetting);
|
||||
this.faceLandmarkerPool = new FaceLandmarkerPool(faceLandmarkerPoolConfSetting);
|
||||
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
faceDetectorPool.setMaxTotal(predictorPoolSize);
|
||||
faceLandmarkerPool.setMaxTotal(predictorPoolSize);
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new FaceException(e);
|
||||
}
|
||||
@@ -212,8 +217,13 @@ public class SeetaFace6FaceDetModel implements FaceDetModel{
|
||||
}
|
||||
|
||||
|
||||
public FaceDetectorPool getFaceDetectorPool() {
|
||||
return faceDetectorPool;
|
||||
}
|
||||
|
||||
|
||||
public FaceLandmarkerPool getFaceLandmarkerPool() {
|
||||
return faceLandmarkerPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
|
||||
@@ -31,7 +31,7 @@ public class FaceDetCriteriaFactory {
|
||||
public static Criteria<Image, DetectedObjects> createCriteria(FaceDetConfig config) {
|
||||
Device device = null;
|
||||
if(!Objects.isNull(config.getDevice())){
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu();
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu(config.getGpuId());
|
||||
}
|
||||
Criteria<Image, DetectedObjects> criteria = null;
|
||||
if(config.getModelEnum() == FaceDetModelEnum.RETINA_FACE){
|
||||
|
||||
@@ -9,6 +9,7 @@ import ai.djl.ndarray.NDManager;
|
||||
import ai.djl.repository.zoo.Criteria;
|
||||
import ai.djl.repository.zoo.ModelNotFoundException;
|
||||
import ai.djl.repository.zoo.ZooModel;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
import cn.smartjavaai.common.entity.*;
|
||||
import cn.smartjavaai.common.entity.face.FaceInfo;
|
||||
import cn.smartjavaai.common.entity.face.FaceSearchResult;
|
||||
@@ -41,6 +42,7 @@ 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 org.opencv.core.Mat;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -68,7 +70,7 @@ public class CommonFaceRecModel implements FaceRecModel{
|
||||
private static volatile boolean isLoadCompleted = false;
|
||||
|
||||
|
||||
private ObjectPool<Predictor<Image, float[]>> predictorPool;
|
||||
private GenericObjectPool<Predictor<Image, float[]>> predictorPool;
|
||||
|
||||
|
||||
private ZooModel<Image, float[]> model;
|
||||
@@ -101,8 +103,14 @@ public class CommonFaceRecModel implements FaceRecModel{
|
||||
model = faceFeatureCriteria.loadModel();
|
||||
// 创建池子:每个线程独享 Predictor
|
||||
this.predictorPool = new GenericObjectPool<>(new PredictorFactory<>(model));
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
predictorPool.setMaxTotal(predictorPoolSize);
|
||||
log.debug("当前设备: " + model.getNDManager().getDevice());
|
||||
log.debug("当前引擎: " + Engine.getInstance().getEngineName());
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
} catch (IOException | ModelNotFoundException | MalformedModelException e) {
|
||||
throw new FaceException("模型加载失败", e);
|
||||
}
|
||||
@@ -286,6 +294,7 @@ public class CommonFaceRecModel implements FaceRecModel{
|
||||
faceInfo.setFeature(features);
|
||||
}
|
||||
}
|
||||
((Mat)djlImage.getWrappedImage()).release();
|
||||
return detectedResult;
|
||||
}
|
||||
|
||||
@@ -350,6 +359,7 @@ public class CommonFaceRecModel implements FaceRecModel{
|
||||
}
|
||||
features = featureExtraction(subImage);
|
||||
}
|
||||
((Mat)djlImage.getWrappedImage()).release();
|
||||
return Objects.isNull(features) ? R.fail(R.Status.Unknown) : R.ok(features);
|
||||
}
|
||||
|
||||
@@ -703,5 +713,8 @@ public class CommonFaceRecModel implements FaceRecModel{
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public GenericObjectPool<Predictor<Image, float[]>> getPool() {
|
||||
return predictorPool;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package cn.smartjavaai.face.model.facerec;
|
||||
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.face.config.FaceRecConfig;
|
||||
@@ -7,6 +9,7 @@ import cn.smartjavaai.face.entity.FaceRegisterInfo;
|
||||
import cn.smartjavaai.face.entity.FaceSearchParams;
|
||||
import cn.smartjavaai.common.entity.face.FaceSearchResult;
|
||||
import cn.smartjavaai.face.vector.entity.FaceVector;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.InputStream;
|
||||
@@ -367,4 +370,9 @@ public interface FaceRecModel extends AutoCloseable{
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
|
||||
default GenericObjectPool<Predictor<Image, float[]>> getPool() {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.model.facerec;
|
||||
|
||||
import ai.djl.engine.Engine;
|
||||
import cn.smartjavaai.common.entity.DetectionInfo;
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
@@ -89,13 +90,9 @@ public class SeetaFace6FaceRecModel implements FaceRecModel{
|
||||
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;
|
||||
int gpuId = config.getGpuId();
|
||||
if(Objects.nonNull(config.getDevice())){
|
||||
device = config.getDevice() == DeviceEnum.CPU ? SeetaDevice.SEETA_DEVICE_CPU : SeetaDevice.SEETA_DEVICE_GPU;
|
||||
Integer gpuIdValue = config.getCustomParam("gpuId", Integer.class);
|
||||
if(Objects.nonNull(gpuIdValue) && device == SeetaDevice.SEETA_DEVICE_GPU){
|
||||
gpuId = gpuIdValue;
|
||||
}
|
||||
}
|
||||
try {
|
||||
SeetaModelSetting faceDetectorPoolSetting = new SeetaModelSetting(gpuId, faceDetectorModelPath, device);
|
||||
@@ -115,6 +112,16 @@ public class SeetaFace6FaceRecModel implements FaceRecModel{
|
||||
this.faceLandmarkerPool = new FaceLandmarkerPool(faceLandmarkerPoolConfSetting);
|
||||
this.faceDatabasePool = new FaceDatabasePool(faceDatabasePoolConfSetting);
|
||||
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
faceDetectorPool.setMaxTotal(predictorPoolSize);
|
||||
faceRecognizerPool.setMaxTotal(predictorPoolSize);
|
||||
faceLandmarkerPool.setMaxTotal(predictorPoolSize);
|
||||
faceDatabasePool.setMaxTotal(predictorPoolSize);
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
|
||||
|
||||
//初始化人脸库
|
||||
if(config.getVectorDBConfig() != null && config.getVectorDBConfig().getType() != null){
|
||||
@@ -739,6 +746,36 @@ public class SeetaFace6FaceRecModel implements FaceRecModel{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 特征提取
|
||||
* @param image
|
||||
* @return
|
||||
*/
|
||||
public float[] featureExtraction(BufferedImage image){
|
||||
FaceRecognizer faceRecognizer = null;
|
||||
try {
|
||||
faceRecognizer = faceRecognizerPool.borrowObject();
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||
//提取特征
|
||||
float[] features = new float[faceRecognizer.GetExtractFeatureSize()];
|
||||
faceRecognizer.ExtractCroppedFace(imageData, features);
|
||||
return features;
|
||||
} catch (FaceException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new FaceException("目标检测错误", e);
|
||||
}finally {
|
||||
if (faceRecognizer != null) {
|
||||
try {
|
||||
faceRecognizerPool.returnObject(faceRecognizer); //归还
|
||||
} catch (Exception e) {
|
||||
log.warn("归还Predictor失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<float[]> extractTopFaceFeature(String imagePath) {
|
||||
if(!FileUtils.isFileExists(imagePath)){
|
||||
@@ -878,4 +915,19 @@ public class SeetaFace6FaceRecModel implements FaceRecModel{
|
||||
}
|
||||
|
||||
|
||||
public FaceDetectorPool getFaceDetectorPool() {
|
||||
return faceDetectorPool;
|
||||
}
|
||||
|
||||
public FaceRecognizerPool getFaceRecognizerPool() {
|
||||
return faceRecognizerPool;
|
||||
}
|
||||
|
||||
public FaceLandmarkerPool getFaceLandmarkerPool() {
|
||||
return faceLandmarkerPool;
|
||||
}
|
||||
|
||||
public FaceDatabasePool getFaceDatabasePool() {
|
||||
return faceDatabasePool;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class FaceRecCriteriaFactory {
|
||||
public static Criteria<Image, float[]> createCriteria(FaceRecConfig config) {
|
||||
Device device = null;
|
||||
if(!Objects.isNull(config.getDevice())){
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu();
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu(config.getGpuId());
|
||||
}
|
||||
Criteria<Image, float[]> criteria = null;
|
||||
if(config.getModelEnum() == FaceRecModelEnum.FACENET_MODEL){
|
||||
@@ -58,6 +58,7 @@ public class FaceRecCriteriaFactory {
|
||||
.optTranslator(new FaceFeatureTranslator())
|
||||
.optEngine("PyTorch") // Use PyTorch engine
|
||||
.optProgress(new ProgressBar())
|
||||
.optDevice(device)
|
||||
.build();
|
||||
}else if (config.getModelEnum() == FaceRecModelEnum.INSIGHT_FACE_IRSE50_MODEL){
|
||||
if(StringUtils.isBlank(config.getModelPath())){
|
||||
@@ -73,6 +74,7 @@ public class FaceRecCriteriaFactory {
|
||||
// .optArgument("resize", "112,112")
|
||||
.optTranslator(new FaceFeatureTranslator())
|
||||
.optEngine("PyTorch") // Use PyTorch engine
|
||||
.optDevice(device)
|
||||
.optProgress(new ProgressBar())
|
||||
.build();
|
||||
}else if (config.getModelEnum() == FaceRecModelEnum.ELASTIC_FACE_MODEL){
|
||||
@@ -89,6 +91,7 @@ public class FaceRecCriteriaFactory {
|
||||
// .optArgument("resize", "112,112")
|
||||
.optTranslator(new FaceFeatureTranslator())
|
||||
.optEngine("PyTorch") // Use PyTorch engine
|
||||
.optDevice(device)
|
||||
.optProgress(new ProgressBar())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public final class FaceFeatureTranslator implements Translator<Image, float[]> {
|
||||
NDArray array = input.toNDArray(ctx.getNDManager(), Image.Flag.COLOR);
|
||||
Pipeline pipeline = new Pipeline();
|
||||
if(input.getWidth() != 112 || input.getHeight() != 112){
|
||||
pipeline.add(new Resize(112));
|
||||
pipeline.add(new Resize(112,112));
|
||||
}
|
||||
pipeline
|
||||
.add(new ToTensor())
|
||||
|
||||
@@ -2,6 +2,7 @@ package cn.smartjavaai.face.model.liveness;
|
||||
|
||||
import ai.djl.Device;
|
||||
import ai.djl.MalformedModelException;
|
||||
import ai.djl.engine.Engine;
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
@@ -35,6 +36,7 @@ import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.bytedeco.javacv.FFmpegFrameGrabber;
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.bytedeco.javacv.Java2DFrameUtils;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -54,7 +56,7 @@ import java.util.*;
|
||||
@Slf4j
|
||||
public class CommonLivenessModel implements LivenessDetModel{
|
||||
|
||||
protected ObjectPool<Predictor<Image, Float>> predictorPool;
|
||||
protected GenericObjectPool<Predictor<Image, Float>> predictorPool;
|
||||
|
||||
protected LivenessConfig config;
|
||||
|
||||
@@ -78,6 +80,15 @@ public class CommonLivenessModel implements LivenessDetModel{
|
||||
} catch (IOException | ModelNotFoundException | MalformedModelException e) {
|
||||
throw new FaceException("阿里通义实验室活体检测模型加载失败", e);
|
||||
}
|
||||
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
predictorPool.setMaxTotal(predictorPoolSize);
|
||||
log.debug("当前设备: " + model.getNDManager().getDevice());
|
||||
log.debug("当前引擎: " + Engine.getInstance().getEngineName());
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +98,7 @@ public class CommonLivenessModel implements LivenessDetModel{
|
||||
throw new FaceException("图像无效");
|
||||
}
|
||||
Predictor<Image, Float> predictor = null;
|
||||
Image djlImage = null;
|
||||
try {
|
||||
predictor = predictorPool.borrowObject();
|
||||
//预处理图片
|
||||
@@ -101,7 +113,7 @@ public class CommonLivenessModel implements LivenessDetModel{
|
||||
.setCenterCropSize(112)
|
||||
.process();
|
||||
}
|
||||
Image djlImage = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(processedImage));
|
||||
djlImage = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(processedImage));
|
||||
Float result = predictor.predict(djlImage);
|
||||
if(result >= config.getRealityThreshold()){
|
||||
return R.ok(new LivenessResult(LivenessStatus.LIVE, result));
|
||||
@@ -124,6 +136,10 @@ public class CommonLivenessModel implements LivenessDetModel{
|
||||
}
|
||||
}
|
||||
}
|
||||
if (djlImage != null){
|
||||
((Mat)djlImage.getWrappedImage()).release();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +419,12 @@ public class CommonLivenessModel implements LivenessDetModel{
|
||||
return R.fail(R.Status.Unknown);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public GenericObjectPool<Predictor<Image, Float>> getPool() {
|
||||
return predictorPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
try {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package cn.smartjavaai.face.model.liveness;
|
||||
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import cn.smartjavaai.common.entity.DetectionRectangle;
|
||||
import cn.smartjavaai.common.entity.DetectionResponse;
|
||||
import cn.smartjavaai.common.entity.Point;
|
||||
import cn.smartjavaai.common.entity.R;
|
||||
import cn.smartjavaai.common.entity.face.LivenessResult;
|
||||
import cn.smartjavaai.face.config.LivenessConfig;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.InputStream;
|
||||
@@ -292,6 +295,9 @@ public interface LivenessDetModel extends AutoCloseable{
|
||||
|
||||
|
||||
|
||||
default GenericObjectPool<Predictor<Image, Float>> getPool() {
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package cn.smartjavaai.face.model.liveness;
|
||||
|
||||
import ai.djl.Device;
|
||||
import ai.djl.MalformedModelException;
|
||||
import ai.djl.engine.Engine;
|
||||
import ai.djl.inference.Predictor;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.ImageFactory;
|
||||
@@ -33,6 +34,7 @@ import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.bytedeco.javacv.FFmpegFrameGrabber;
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.bytedeco.javacv.Java2DFrameUtils;
|
||||
import org.opencv.core.Mat;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -58,9 +60,9 @@ public class MiniVisionLivenessModel extends CommonLivenessModel{
|
||||
*/
|
||||
private static final String SE_MODEL_PATH_KEY = "seModelPath";
|
||||
|
||||
private ObjectPool<Predictor<Image, float[]>> predictorPool;
|
||||
private GenericObjectPool<Predictor<Image, float[]>> predictorPool;
|
||||
|
||||
private ObjectPool<Predictor<Image, float[]>> sePredictorPool;
|
||||
private GenericObjectPool<Predictor<Image, float[]>> sePredictorPool;
|
||||
|
||||
|
||||
/**
|
||||
@@ -87,7 +89,7 @@ public class MiniVisionLivenessModel extends CommonLivenessModel{
|
||||
this.config.setRealityThreshold(realityThreshold);
|
||||
Device device = null;
|
||||
if(!Objects.isNull(config.getDevice())){
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu();
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu(config.getGpuId());
|
||||
}
|
||||
if(StringUtils.isNotBlank(config.getModelPath()) && StringUtils.isBlank(seModelPath)){
|
||||
//2.7_80x80_MiniFASNetV2
|
||||
@@ -109,6 +111,7 @@ public class MiniVisionLivenessModel extends CommonLivenessModel{
|
||||
.optModelPath(Paths.get(config.getModelPath()))
|
||||
.optTranslator(new MiniVisionTranslator())
|
||||
.optProgress(new ProgressBar())
|
||||
.optDevice(device)
|
||||
.build();
|
||||
try {
|
||||
model = criteria.loadModel();
|
||||
@@ -128,6 +131,7 @@ public class MiniVisionLivenessModel extends CommonLivenessModel{
|
||||
.optModelPath(Paths.get(seModelPath))
|
||||
.optTranslator(new MiniVisionTranslator())
|
||||
.optProgress(new ProgressBar())
|
||||
.optDevice(device)
|
||||
.build();
|
||||
try {
|
||||
seModel = seCriteria.loadModel();
|
||||
@@ -136,6 +140,16 @@ public class MiniVisionLivenessModel extends CommonLivenessModel{
|
||||
throw new FaceException("MiniFASNetV1SE模型加载失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
predictorPool.setMaxTotal(predictorPoolSize);
|
||||
sePredictorPool.setMaxTotal(predictorPoolSize);
|
||||
log.debug("当前设备: " + model.getNDManager().getDevice());
|
||||
log.debug("当前引擎: " + Engine.getInstance().getEngineName());
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +174,8 @@ public class MiniVisionLivenessModel extends CommonLivenessModel{
|
||||
predictor = predictorPool.borrowObject();
|
||||
Image djlImage = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(processedImage));
|
||||
result = predictor.predict(djlImage);
|
||||
((Mat)djlImage.getWrappedImage()).release();
|
||||
|
||||
}
|
||||
if(Objects.nonNull(sePredictorPool)){
|
||||
//预处理图片
|
||||
@@ -172,6 +188,7 @@ public class MiniVisionLivenessModel extends CommonLivenessModel{
|
||||
Image djlImage = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(processedImage));
|
||||
sePredictor = sePredictorPool.borrowObject();
|
||||
seResult = sePredictor.predict(djlImage);
|
||||
((Mat)djlImage.getWrappedImage()).release();
|
||||
}
|
||||
if(Objects.isNull(result) && Objects.isNull(seResult)){
|
||||
throw new FaceException("活体检测错误");
|
||||
@@ -221,6 +238,14 @@ public class MiniVisionLivenessModel extends CommonLivenessModel{
|
||||
}
|
||||
}
|
||||
|
||||
public GenericObjectPool<Predictor<Image, float[]>> getPredictorPool() {
|
||||
return predictorPool;
|
||||
}
|
||||
|
||||
public GenericObjectPool<Predictor<Image, float[]>> getSePredictorPool() {
|
||||
return sePredictorPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.model.liveness;
|
||||
|
||||
import ai.djl.engine.Engine;
|
||||
import cn.smartjavaai.common.entity.*;
|
||||
import cn.smartjavaai.common.entity.face.FaceInfo;
|
||||
import cn.smartjavaai.common.entity.face.LivenessResult;
|
||||
@@ -56,13 +57,9 @@ public class Seetaface6LivenessModel implements LivenessDetModel{
|
||||
String[] faceAntiSpoofingModelPath = {config.getModelPath() + File.separator + "fas_first.csta",config.getModelPath() + File.separator + "fas_second.csta"};
|
||||
String[] faceLandmarkerModelPath = {config.getModelPath() + File.separator + "face_landmarker_pts5.csta"};
|
||||
SeetaDevice device = SeetaDevice.SEETA_DEVICE_AUTO;
|
||||
int gpuId = 0;
|
||||
int gpuId = config.getGpuId();
|
||||
if(Objects.nonNull(config.getDevice())){
|
||||
device = config.getDevice() == DeviceEnum.CPU ? SeetaDevice.SEETA_DEVICE_CPU : SeetaDevice.SEETA_DEVICE_GPU;
|
||||
Integer gpuIdValue = config.getCustomParam("gpuId", Integer.class);
|
||||
if(Objects.nonNull(gpuIdValue) && device == SeetaDevice.SEETA_DEVICE_GPU){
|
||||
gpuId = gpuIdValue;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -79,6 +76,16 @@ public class Seetaface6LivenessModel implements LivenessDetModel{
|
||||
this.faceAntiSpoofingPool = new FaceAntiSpoofingPool(faceAntiSpoofingPoolConfSetting);
|
||||
this.faceLandmarkerPool = new FaceLandmarkerPool(faceLandmarkerPoolConfSetting);
|
||||
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
|
||||
faceDetectorPool.setMaxTotal(predictorPoolSize);
|
||||
faceAntiSpoofingPool.setMaxTotal(predictorPoolSize);
|
||||
faceLandmarkerPool.setMaxTotal(predictorPoolSize);
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
|
||||
//初始化模型参数
|
||||
initConfig();
|
||||
} catch (FileNotFoundException e) {
|
||||
@@ -550,6 +557,18 @@ public class Seetaface6LivenessModel implements LivenessDetModel{
|
||||
return R.fail(1000, "有效帧数量不足,无法完成活体检测");
|
||||
}
|
||||
|
||||
public FaceDetectorPool getFaceDetectorPool() {
|
||||
return faceDetectorPool;
|
||||
}
|
||||
|
||||
public FaceAntiSpoofingPool getFaceAntiSpoofingPool() {
|
||||
return faceAntiSpoofingPool;
|
||||
}
|
||||
|
||||
public FaceLandmarkerPool getFaceLandmarkerPool() {
|
||||
return faceLandmarkerPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
try {
|
||||
|
||||
@@ -31,7 +31,7 @@ public class LivenessCriteriaFactory {
|
||||
public static Criteria<Image, Float> createCriteria(LivenessConfig config) {
|
||||
Device device = null;
|
||||
if(!Objects.isNull(config.getDevice())){
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu();
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu(config.getGpuId());
|
||||
}
|
||||
Criteria<Image, Float> criteria = null;
|
||||
if(config.getModelEnum() == LivenessModelEnum.IIC_FL_MODEL){
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.smartjavaai.face.model.quality;
|
||||
|
||||
import ai.djl.engine.Engine;
|
||||
import cn.smartjavaai.common.entity.*;
|
||||
import cn.smartjavaai.common.enums.DeviceEnum;
|
||||
import cn.smartjavaai.common.utils.FileUtils;
|
||||
@@ -74,6 +75,8 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
*/
|
||||
private QualityOfResolutionPool qualityOfResolutionPool;
|
||||
|
||||
int predictorPoolSize = 0;
|
||||
|
||||
|
||||
@Override
|
||||
public void loadModel(QualityConfig config) {
|
||||
@@ -84,6 +87,11 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
//加载依赖库
|
||||
NativeLoader.loadNativeLibraries(device);
|
||||
this.config = config;
|
||||
int predictorPoolSize = config.getPredictorPoolSize();
|
||||
if(config.getPredictorPoolSize() <= 0){
|
||||
predictorPoolSize = Runtime.getRuntime().availableProcessors(); // 默认等于CPU核心数
|
||||
}
|
||||
log.debug("模型推理器线程池最大数量: " + predictorPoolSize);
|
||||
log.debug("Loading seetaFace6 library successfully.");
|
||||
}
|
||||
|
||||
@@ -104,6 +112,7 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
try {
|
||||
if(Objects.isNull(this.qualityOfBrightnessPool)){
|
||||
this.qualityOfBrightnessPool = new QualityOfBrightnessPool(new SeetaConfSetting());
|
||||
qualityOfBrightnessPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
qualityOfBrightness = qualityOfBrightnessPool.borrowObject();
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
@@ -170,6 +179,7 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
try {
|
||||
if(Objects.isNull(this.qualityOfClarityPool)){
|
||||
this.qualityOfClarityPool = new QualityOfClarityPool(new SeetaConfSetting());
|
||||
qualityOfClarityPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
qualityOfClarity = qualityOfClarityPool.borrowObject();
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
@@ -236,6 +246,7 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
try {
|
||||
if(Objects.isNull(this.qualityOfIntegrityPool)){
|
||||
this.qualityOfIntegrityPool = new QualityOfIntegrityPool(new SeetaConfSetting());
|
||||
qualityOfIntegrityPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
qualityOfIntegrity = qualityOfIntegrityPool.borrowObject();
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
@@ -302,6 +313,7 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
try {
|
||||
if(Objects.isNull(this.qualityOfPosePool)){
|
||||
this.qualityOfPosePool = new QualityOfPosePool(new SeetaConfSetting());
|
||||
qualityOfPosePool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
qualityOfPose = qualityOfPosePool.borrowObject();
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
@@ -368,6 +380,7 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
try {
|
||||
if(Objects.isNull(this.qualityOfResolutionPool)){
|
||||
this.qualityOfResolutionPool = new QualityOfResolutionPool(new SeetaConfSetting());
|
||||
qualityOfResolutionPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
qualityOfResolution = qualityOfResolutionPool.borrowObject();
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
@@ -438,6 +451,7 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
}
|
||||
SeetaConfSetting setting = getClarityMLSetting();
|
||||
this.qualityOfLBNPool = new QualityOfLBNPool(setting);
|
||||
qualityOfLBNPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
qualityOfLBN = qualityOfLBNPool.borrowObject();
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
@@ -509,6 +523,7 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
}
|
||||
SeetaConfSetting setting = getPoseMLSetting();
|
||||
this.qualityOfPoseExPool = new QualityOfPoseExPool(setting);
|
||||
qualityOfPoseExPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
qualityOfPoseEx = qualityOfPoseExPool.borrowObject();
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
@@ -628,18 +643,23 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
try {
|
||||
if(Objects.isNull(this.qualityOfBrightnessPool)){
|
||||
this.qualityOfBrightnessPool = new QualityOfBrightnessPool(new SeetaConfSetting());
|
||||
qualityOfBrightnessPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
if(Objects.isNull(this.qualityOfClarityPool)){
|
||||
this.qualityOfClarityPool = new QualityOfClarityPool(new SeetaConfSetting());
|
||||
qualityOfClarityPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
if(Objects.isNull(this.qualityOfIntegrityPool)){
|
||||
this.qualityOfIntegrityPool = new QualityOfIntegrityPool(new SeetaConfSetting());
|
||||
qualityOfIntegrityPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
if(Objects.isNull(this.qualityOfPosePool)){
|
||||
this.qualityOfPosePool = new QualityOfPosePool(new SeetaConfSetting());
|
||||
qualityOfPosePool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
if(Objects.isNull(this.qualityOfResolutionPool)){
|
||||
this.qualityOfResolutionPool = new QualityOfResolutionPool(new SeetaConfSetting());
|
||||
qualityOfResolutionPool.setMaxTotal(predictorPoolSize);
|
||||
}
|
||||
FaceQualitySummary summary = new FaceQualitySummary();
|
||||
qualityOfBrightness = qualityOfBrightnessPool.borrowObject();
|
||||
@@ -690,6 +710,34 @@ public class Seetaface6QualityModel implements FaceQualityModel {
|
||||
}
|
||||
}
|
||||
|
||||
public QualityOfBrightnessPool getQualityOfBrightnessPool() {
|
||||
return qualityOfBrightnessPool;
|
||||
}
|
||||
|
||||
public QualityOfClarityPool getQualityOfClarityPool() {
|
||||
return qualityOfClarityPool;
|
||||
}
|
||||
|
||||
public QualityOfLBNPool getQualityOfLBNPool() {
|
||||
return qualityOfLBNPool;
|
||||
}
|
||||
|
||||
public QualityOfIntegrityPool getQualityOfIntegrityPool() {
|
||||
return qualityOfIntegrityPool;
|
||||
}
|
||||
|
||||
public QualityOfPosePool getQualityOfPosePool() {
|
||||
return qualityOfPosePool;
|
||||
}
|
||||
|
||||
public QualityOfPoseExPool getQualityOfPoseExPool() {
|
||||
return qualityOfPoseExPool;
|
||||
}
|
||||
|
||||
public QualityOfResolutionPool getQualityOfResolutionPool() {
|
||||
return qualityOfResolutionPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if(Objects.nonNull(qualityOfBrightnessPool)){
|
||||
|
||||
@@ -60,15 +60,13 @@ public class DJLImagePreprocessor {
|
||||
// 处理流程
|
||||
public Image process() {
|
||||
Image result = image;
|
||||
|
||||
if (enableCrop) {
|
||||
result = result.getSubImage(cropRect.x, cropRect.y, cropRect.width, cropRect.height);
|
||||
}
|
||||
|
||||
if (enableAffine) {
|
||||
if(enableAffine){
|
||||
result = warpAffine(keyPoints, affineTargetWidth, affineTargetHeight);
|
||||
}else {
|
||||
if(enableCrop){
|
||||
result = result.getSubImage(cropRect.x, cropRect.y, cropRect.width, cropRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,16 @@ public class MilvusConfig extends VectorDBConfig {
|
||||
*/
|
||||
private boolean useMemoryCache = true;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
|
||||
@@ -49,10 +49,13 @@ public class MilvusClient implements VectorDBClient {
|
||||
@Override
|
||||
public void initialize() {
|
||||
try {
|
||||
ConnectParam connectParam = ConnectParam.newBuilder()
|
||||
ConnectParam.Builder builder = ConnectParam.newBuilder()
|
||||
.withHost(config.getHost())
|
||||
.withPort(config.getPort())
|
||||
.build();
|
||||
.withPort(config.getPort());
|
||||
if (StringUtils.isNotBlank(config.getUsername()) && StringUtils.isNotBlank(config.getPassword())) {
|
||||
builder.withAuthorization(config.getUsername(), config.getPassword());
|
||||
}
|
||||
ConnectParam connectParam = builder.build();
|
||||
serviceClient = new MilvusServiceClient(connectParam);
|
||||
collectionName = StringUtils.isNotBlank(config.getCollectionName()) ? config.getCollectionName() : VectorDBConstants.Defaults.DEFAULT_COLLECTION_NAME;
|
||||
createCollection(collectionName, config.getDimension());
|
||||
|
||||
Reference in New Issue
Block a user