mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-18 16:39:21 +00:00
- 【人脸检测】新增6个模型(MTCNN、YOLOV5、RetinaFace小尺寸版),大幅提升性能
- 【人脸识别】新增Seetaface6轻量模型 - 【目标检测】支持视频流目标检测(rtsp、视频文件等) - 【目标检测】支持tensorflow2目标检测模型 - 【目标检测】新增行人检测模型(yolo-person) - 【通用视觉】新增4个动作识别模型 - 【通用视觉】新增语义分割模型 - 【通用视觉】新增5个实例分割模型(含yolov8-seg、yolov11-seg) - 【通用视觉】新增yolo-obb11旋转框检测(含yolov11-obb) - 【通用视觉】新增5个姿态估计模型(含yolov8-pose、yolov11-pose)
This commit is contained in:
@@ -3,8 +3,10 @@ package cn.smartjavaai.action.criteria;
|
||||
import ai.djl.Device;
|
||||
import ai.djl.modality.Classifications;
|
||||
import ai.djl.modality.cv.Image;
|
||||
import ai.djl.modality.cv.output.DetectedObjects;
|
||||
import ai.djl.repository.zoo.Criteria;
|
||||
import ai.djl.training.util.ProgressBar;
|
||||
import ai.djl.translate.Translator;
|
||||
import cn.smartjavaai.action.config.ActionRecModelConfig;
|
||||
import cn.smartjavaai.action.enums.ActionRecModelEnum;
|
||||
import cn.smartjavaai.action.model.CommonActionTranslator;
|
||||
@@ -23,45 +25,48 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public class ActionRecCriteriaFactory {
|
||||
|
||||
|
||||
/**
|
||||
* 创建动作识别Criteria
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
public static Criteria<Image, Classifications> createCriteria(ActionRecModelConfig config) {
|
||||
Device device = null;
|
||||
if(!Objects.isNull(config.getDevice())){
|
||||
device = config.getDevice() == DeviceEnum.CPU ? Device.cpu() : Device.gpu(config.getGpuId());
|
||||
}
|
||||
Criteria<Image, Classifications> criteria = null;
|
||||
ConcurrentHashMap params = new ConcurrentHashMap<String, String>();
|
||||
params.putAll(config.getCustomParams());
|
||||
if(config.getModelEnum() == ActionRecModelEnum.VIT_BASE_PATCH16_224){
|
||||
criteria =
|
||||
Criteria.builder()
|
||||
.setTypes(Image.class, Classifications.class)
|
||||
.optModelUrls(StringUtils.isNotBlank(config.getModelPath()) ? null :
|
||||
config.getModelEnum().getModelUri())
|
||||
.optModelPath(StringUtils.isNotBlank(config.getModelPath()) ? Paths.get(config.getModelPath()) : null)
|
||||
.optEngine("PyTorch")
|
||||
.optDevice(device)
|
||||
.optProgress(new ProgressBar())
|
||||
.build();
|
||||
}else {
|
||||
Translator<Image, Classifications> translator = getTranslator(config);
|
||||
if(StringUtils.isBlank(config.getModelEnum().getModelUrl())){
|
||||
//检查模型路径
|
||||
if (StringUtils.isBlank(config.getModelPath())){
|
||||
throw new ActionException("请指定模型路径");
|
||||
}
|
||||
int width = 224;
|
||||
int height = 224;
|
||||
if(config.getModelEnum() == ActionRecModelEnum.INCEPTIONV3_KINETICS400){
|
||||
width = 299;
|
||||
height = 299;
|
||||
}
|
||||
criteria =
|
||||
Criteria.builder()
|
||||
.setTypes(Image.class, Classifications.class)
|
||||
.optTranslator(new CommonActionTranslator(width, height))
|
||||
.optEngine("OnnxRuntime")
|
||||
.optModelPath(Paths.get(config.getModelPath()))
|
||||
.optDevice(device)
|
||||
.optProgress(new ProgressBar())
|
||||
.build();
|
||||
}
|
||||
Criteria<Image, Classifications> criteria =
|
||||
Criteria.builder()
|
||||
.setTypes(Image.class, Classifications.class)
|
||||
.optModelUrls(StringUtils.isNotBlank(config.getModelPath()) ? null : config.getModelEnum().getModelUrl())
|
||||
.optModelPath(StringUtils.isNotBlank(config.getModelPath()) ? Paths.get(config.getModelPath()) : null)
|
||||
.optTranslator(translator)
|
||||
.optDevice(device)
|
||||
.optProgress(new ProgressBar())
|
||||
.optEngine(config.getModelEnum().getEngine())
|
||||
.build();
|
||||
return criteria;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动作识别Translator
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
public static Translator<Image, Classifications> getTranslator(ActionRecModelConfig config) {
|
||||
Translator<Image, Classifications> translator = null;
|
||||
if(config.getModelEnum() == ActionRecModelEnum.INCEPTIONV1_KINETICS400_ONNX
|
||||
|| config.getModelEnum() == ActionRecModelEnum.INCEPTIONV3_KINETICS400_ONNX
|
||||
|| config.getModelEnum() == ActionRecModelEnum.INCEPTIONV3_KINETICS400_ONNX){
|
||||
translator =new CommonActionTranslator(config.getModelEnum().getInputWidth(), config.getModelEnum().getInputHeight());
|
||||
}
|
||||
return translator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,33 @@ package cn.smartjavaai.action.enums;
|
||||
*/
|
||||
public enum ActionRecModelEnum {
|
||||
|
||||
VIT_BASE_PATCH16_224("djl://ai.djl.pytorch/Human-Action-Recognition-VIT-Base-patch16-224"),
|
||||
VIT_BASE_PATCH16_224_DJL("PyTorch",0,0,"djl://ai.djl.pytorch/Human-Action-Recognition-VIT-Base-patch16-224"),
|
||||
|
||||
INCEPTIONV3_KINETICS400(""),
|
||||
INCEPTIONV3_KINETICS400_ONNX("OnnxRuntime",299,299,""),
|
||||
|
||||
INCEPTIONV1_KINETICS400(""),
|
||||
INCEPTIONV1_KINETICS400_ONNX("OnnxRuntime",224,224,""),
|
||||
|
||||
RESNET18_V1B_KINETICS400(""),
|
||||
RESNET_V1B_KINETICS400_ONNX("OnnxRuntime",224,224,"");
|
||||
|
||||
RESNET34_V1B_KINETICS400(""),
|
||||
/**
|
||||
* 模型输入尺寸:宽
|
||||
*/
|
||||
private final int inputWidth;
|
||||
|
||||
RESNET50_V1B_KINETICS400(""),
|
||||
/**
|
||||
* 模型输入尺寸:高
|
||||
*/
|
||||
private final int inputHeight;
|
||||
|
||||
RESNET101_V1B_KINETICS400(""),
|
||||
/**
|
||||
* 模型地址
|
||||
*/
|
||||
private final String modelUrl;
|
||||
|
||||
RESNET152_V1B_KINETICS400("");
|
||||
/**
|
||||
* 模型引擎
|
||||
*/
|
||||
private final String engine;
|
||||
|
||||
/**
|
||||
* 根据名称获取枚举 (忽略大小写和下划线变体)
|
||||
@@ -35,14 +47,27 @@ public enum ActionRecModelEnum {
|
||||
throw new IllegalArgumentException("未知模型名称: " + name);
|
||||
}
|
||||
|
||||
private final String modelUri;
|
||||
|
||||
ActionRecModelEnum(String modelUri) {
|
||||
this.modelUri = modelUri;
|
||||
ActionRecModelEnum(String engine, int inputWidth, int inputHeight, String modelUrl) {
|
||||
this.inputWidth = inputWidth;
|
||||
this.inputHeight = inputHeight;
|
||||
this.modelUrl = modelUrl;
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
public String getModelUri() {
|
||||
return modelUri;
|
||||
public int getInputWidth() {
|
||||
return inputWidth;
|
||||
}
|
||||
|
||||
public int getInputHeight() {
|
||||
return inputHeight;
|
||||
}
|
||||
|
||||
public String getModelUrl() {
|
||||
return modelUrl;
|
||||
}
|
||||
|
||||
public String getEngine() {
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,15 +24,6 @@ public interface ActionRecModel extends AutoCloseable{
|
||||
*/
|
||||
void loadModel(ActionRecModelConfig config);
|
||||
|
||||
/**
|
||||
* 动作检测
|
||||
* @param base64Image
|
||||
* @return
|
||||
*/
|
||||
default R<Classifications> detectBase64(String base64Image){
|
||||
throw new UnsupportedOperationException("默认不支持该功能");
|
||||
}
|
||||
|
||||
/**
|
||||
* 动作检测
|
||||
* @param image
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package cn.smartjavaai.action.model;
|
||||
|
||||
import cn.smartjavaai.action.config.ActionRecModelConfig;
|
||||
import cn.smartjavaai.action.enums.ActionRecModelEnum;
|
||||
import cn.smartjavaai.common.config.Config;
|
||||
import cn.smartjavaai.objectdetection.exception.DetectionException;
|
||||
import cn.smartjavaai.objectdetection.model.person.CommonPersonDetModel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 动作识别 模型工厂
|
||||
* @author dwj
|
||||
*/
|
||||
@Slf4j
|
||||
public class ActionRecModelFactory {
|
||||
|
||||
// 使用 volatile 和双重检查锁定来确保线程安全的单例模式
|
||||
private static volatile ActionRecModelFactory instance;
|
||||
|
||||
private static final ConcurrentHashMap<ActionRecModelEnum, ActionRecModel> modelMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 模型注册表
|
||||
*/
|
||||
private static final Map<ActionRecModelEnum, Class<? extends ActionRecModel>> registry =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
// 私有构造函数,防止外部创建实例
|
||||
private ActionRecModelFactory() {}
|
||||
|
||||
// 双重检查锁定的单例方法
|
||||
public static ActionRecModelFactory getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (ActionRecModelFactory.class) {
|
||||
if (instance == null) {
|
||||
instance = new ActionRecModelFactory();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模型(通过配置)
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
public ActionRecModel getModel(ActionRecModelConfig config) {
|
||||
if(Objects.isNull(config) || Objects.isNull(config.getModelEnum())){
|
||||
throw new DetectionException("未配置模型");
|
||||
}
|
||||
return modelMap.computeIfAbsent(config.getModelEnum(), k -> {
|
||||
return createFaceDetModel(config);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用ModelConfig创建模型
|
||||
* @param config
|
||||
* @return
|
||||
*/
|
||||
private ActionRecModel createFaceDetModel(ActionRecModelConfig config) {
|
||||
Class<?> clazz = registry.get(config.getModelEnum());
|
||||
if(clazz == null){
|
||||
throw new DetectionException("Unsupported model");
|
||||
}
|
||||
ActionRecModel model = null;
|
||||
try {
|
||||
model = (ActionRecModel) clazz.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new DetectionException(e);
|
||||
}
|
||||
model.loadModel(config);
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 注册模型
|
||||
* @param modelEnum
|
||||
* @param clazz
|
||||
*/
|
||||
private static void registerAlgorithm(ActionRecModelEnum modelEnum, Class<? extends ActionRecModel> clazz) {
|
||||
registry.put(modelEnum, clazz);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 移除缓存的模型
|
||||
* @param modelEnum
|
||||
*/
|
||||
public static void removeFromCache(ActionRecModelEnum modelEnum) {
|
||||
modelMap.remove(modelEnum);
|
||||
}
|
||||
|
||||
|
||||
// 初始化默认算法
|
||||
static {
|
||||
registerAlgorithm(ActionRecModelEnum.INCEPTIONV1_KINETICS400_ONNX, CommonActionRecModel.class);
|
||||
registerAlgorithm(ActionRecModelEnum.INCEPTIONV3_KINETICS400_ONNX, CommonActionRecModel.class);
|
||||
registerAlgorithm(ActionRecModelEnum.RESNET_V1B_KINETICS400_ONNX, CommonActionRecModel.class);
|
||||
registerAlgorithm(ActionRecModelEnum.VIT_BASE_PATCH16_224_DJL, CommonActionRecModel.class);
|
||||
log.debug("缓存目录:{}", Config.getCachePath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,26 +68,12 @@ public class CommonActionRecModel implements ActionRecModel{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Classifications> detectBase64(String base64Image) {
|
||||
if(StringUtils.isBlank(base64Image)){
|
||||
return R.fail(R.Status.INVALID_IMAGE);
|
||||
}
|
||||
try {
|
||||
byte[] imageData = Base64ImageUtils.base64ToImage(base64Image);
|
||||
Image image = ImageFactory.getInstance().fromInputStream(new ByteArrayInputStream(imageData));
|
||||
return detect(image);
|
||||
} catch (IOException e) {
|
||||
throw new DetectionException("读取图片异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Classifications> detect(Image image) {
|
||||
Classifications classifications = detectCore(image);
|
||||
// 过滤
|
||||
if(config.getThreshold() > 0 && CollectionUtils.isNotEmpty(config.getAllowedClasses())
|
||||
&& Objects.nonNull(classifications) && !classifications.items().isEmpty()){
|
||||
if(Objects.nonNull(classifications) && !classifications.items().isEmpty()){
|
||||
classifications = new ClassificationFilter(config.getAllowedClasses(), config.getThreshold()).filter(classifications);
|
||||
}
|
||||
return R.ok(classifications);
|
||||
|
||||
@@ -116,7 +116,6 @@ public class CommonActionTranslator implements Translator<Image, Classifications
|
||||
float[] std = {0.229f * 255, 0.224f * 255, 0.225f * 255};
|
||||
// 增加 batch 维度,变成 (1, H, W, C)
|
||||
array = array.expandDims(0);
|
||||
System.out.println(Arrays.toString(array.getShape().getShape()));
|
||||
return new NDList(array);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user