1.FilePathEnum增加下载路径转实际路径

2.模型配置及检测init
This commit is contained in:
cuizhibin 2025-07-03 14:55:07 +08:00
parent e73257514b
commit 8cba5fe502
33 changed files with 1105 additions and 86 deletions

View File

@ -149,7 +149,20 @@
<version>1.3.0-91</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.microsoft.onnxruntime</groupId>
<artifactId>onnxruntime</artifactId>
<version>1.16.1</version>
</dependency>
<!-- javacv 和 opencv 别同时用 -->
<dependency>
<groupId>org.openpnp</groupId>
<artifactId>opencv</artifactId>
<version>4.7.0-0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>

View File

@ -1,15 +1,12 @@
package com.dite.znpt.config;
import cn.hutool.core.collection.ListUtil;
import com.dite.znpt.constant.Constants;
import com.dite.znpt.enums.FilePathEnum;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@ -27,7 +24,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
for (FilePathEnum pathEnum : FilePathEnum.values()) {
registry.addResourceHandler(pathEnum.getUrlPath() + "**").addResourceLocations("file:" + pathEnum.getFileAbsolutePath());
registry.addResourceHandler(pathEnum.getUrlPath() + "**").addResourceLocations("file:" + pathEnum.getFileAbsolutePathPrefix());
}
}

View File

@ -0,0 +1,45 @@
package com.dite.znpt.config;
import ai.onnxruntime.OrtEnvironment;
import ai.onnxruntime.OrtException;
import ai.onnxruntime.OrtSession;
import lombok.Getter;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Getter
@Configuration
public class YoloModelConfig {
private OrtEnvironment env;
private OrtSession session;
private String[] labels;
private List<double[]> colors;
@PostConstruct
public void init() throws OrtException {
env = OrtEnvironment.getEnvironment();
OrtSession.SessionOptions options = new OrtSession.SessionOptions();
session = env.createSession("d:\\tmp\\best.onnx", options);
// 解析 labels
String names = session.getMetadata().getCustomMetadata().get("names");
Pattern pattern = Pattern.compile("'([^']*)'");
Matcher matcher = pattern.matcher(names);
List<String> labelList = new ArrayList<>();
colors = new ArrayList<>();
Random random = new Random();
while (matcher.find()) {
labelList.add(matcher.group(1));
colors.add(new double[]{random.nextDouble()*256, random.nextDouble()*256, random.nextDouble()*256});
}
labels = labelList.toArray(new String[0]);
}
}

View File

@ -0,0 +1,112 @@
package com.dite.znpt.config;
import ai.onnxruntime.OrtEnvironment;
import ai.onnxruntime.OrtException;
import ai.onnxruntime.OrtSession;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dite.znpt.domain.entity.ModelConfigEntity;
import com.dite.znpt.enums.FilePathEnum;
import com.dite.znpt.mapper.ModelConfigMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
@Component
@RequiredArgsConstructor
public class YoloModelRegistry {
private final ModelConfigMapper modelConfigMapper;
private final Map<String, OrtSession> sessionMap = new ConcurrentHashMap<>();
private final Map<String, ModelMetadata> metaMap = new ConcurrentHashMap<>();
private final Map<String, ModelConfigEntity> modelParamMap = new ConcurrentHashMap<>();
@Getter
private final OrtEnvironment environment = OrtEnvironment.getEnvironment();
static {
// 加载opencv动态库
nu.pattern.OpenCV.loadLocally();
}
@PostConstruct
public void loadModelsOnStartup() throws OrtException {
List<ModelConfigEntity> configs = modelConfigMapper.selectList(Wrappers.emptyWrapper());
for (ModelConfigEntity config : configs) {
loadModel(config);
}
}
public void loadModel(ModelConfigEntity config) throws OrtException {
OrtSession.SessionOptions opts = new OrtSession.SessionOptions();
// 使用gpu,需要本机安装过cuda并修改pom.xml不安装也能运行本程序
// sessionOptions.addCUDA(0);
OrtSession session = environment.createSession(FilePathEnum.ATTACH.getFileAbsolutePath(config.getModelPath()), opts);
String labelStr = session.getMetadata().getCustomMetadata().get("names");
// label解析
List<String> labels = new ArrayList<>();
List<double[]> colors = new ArrayList<>();
Pattern pattern = Pattern.compile("'([^']*)'");
Matcher matcher = pattern.matcher(labelStr);
Random random = new Random();
while (matcher.find()) {
labels.add(matcher.group(1));
colors.add(new double[]{random.nextDouble() * 256, random.nextDouble() * 256, random.nextDouble() * 256});
}
sessionMap.put(config.getModelId(), session);
metaMap.put(config.getModelId(), new ModelMetadata(labels.toArray(new String[0]), colors, config));
modelParamMap.put(config.getModelId(), config);
}
public OrtSession getSession(String modelId) {
return sessionMap.get(modelId);
}
public ModelMetadata getMetadata(String modelId) {
return metaMap.get(modelId);
}
public ModelConfigEntity getModelConfig(String modelId) {
return modelParamMap.get(modelId);
}
public void unloadModel(String modelId) {
OrtSession session = sessionMap.remove(modelId);
if (session != null) {
try {
session.close(); // 释放 ONNX runtime 资源
} catch (OrtException e) {
log.warn("模型 {} 卸载时发生错误: {}", modelId, e.getMessage());
}
}
metaMap.remove(modelId);
}
public void reloadModel(ModelConfigEntity modelConfig) throws OrtException {
unloadModel(modelConfig.getModelId());
loadModel(modelConfig);
}
@Data
@AllArgsConstructor
public static class ModelMetadata {
private String[] labels;
private List<double[]> colors;
private ModelConfigEntity config;
}
}

View File

@ -0,0 +1,21 @@
package com.dite.znpt.domain.bo;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Detection{
@ApiModelProperty("标签")
public String label;
@ApiModelProperty("分类id")
private Integer clsId;
@ApiModelProperty("位置")
private float[] bbox;
@ApiModelProperty("置信度")
private float confidence;
}

View File

@ -0,0 +1,73 @@
package com.dite.znpt.domain.bo;
import lombok.Getter;
import lombok.Setter;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
public class Letterbox {
@Setter
private Size newShape ;
private final double[] color = new double[]{114,114,114};
private final Boolean auto = false;
private final Boolean scaleUp = true;
@Setter
private Integer stride = 32;
@Getter
private double ratio;
@Getter
private double dw;
@Getter
private double dh;
public Letterbox(int w,int h) {
this.newShape = new Size(w, h);
}
public Letterbox() {
this.newShape = new Size(640, 640);
}
public Integer getWidth() {
return (int) this.newShape.width;
}
public Integer getHeight() {
return (int) this.newShape.height;
}
public Mat letterbox(Mat im) { // 调整图像大小和填充图像使满足步长约束并记录参数
int[] shape = {im.rows(), im.cols()}; // 当前形状 [height, width]
// Scale ratio (new / old)
double r = Math.min(this.newShape.height / shape[0], this.newShape.width / shape[1]);
if (!this.scaleUp) { // 仅缩小不扩大一且为了mAP
r = Math.min(r, 1.0);
}
// Compute padding
Size newUnpad = new Size(Math.round(shape[1] * r), Math.round(shape[0] * r));
double dw = this.newShape.width - newUnpad.width, dh = this.newShape.height - newUnpad.height; // wh 填充
if (this.auto) { // 最小矩形
dw = dw % this.stride;
dh = dh % this.stride;
}
dw /= 2; // 填充的时候两边都填充一半使图像居于中心
dh /= 2;
if (shape[1] != newUnpad.width || shape[0] != newUnpad.height) { // resize
Imgproc.resize(im, im, newUnpad, 0, 0, Imgproc.INTER_LINEAR);
}
int top = (int) Math.round(dh - 0.1), bottom = (int) Math.round(dh + 0.1);
int left = (int) Math.round(dw - 0.1), right = (int) Math.round(dw + 0.1);
// 将图像填充为正方形
Core.copyMakeBorder(im, im, top, bottom, left, right, Core.BORDER_CONSTANT, new org.opencv.core.Scalar(this.color));
this.ratio = r;
this.dh = dh;
this.dw = dw;
return im;
}
}

View File

@ -0,0 +1,55 @@
package com.dite.znpt.domain.bo;
import java.util.*;
public final class ODConfig {
public static final Integer lineThicknessRatio = 333;
public static final Double fontSizeRatio = 1080.0;
private static final List<String> default_names = new ArrayList<>(Arrays.asList(
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train",
"truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter",
"bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear",
"zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase",
"frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat",
"baseball glove", "skateboard", "surfboard", "tennis racket", "bottle",
"wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut",
"cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet",
"tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave",
"oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors",
"teddy bear", "hair drier", "toothbrush"));
private static final List<String> names = new ArrayList<>(Arrays.asList(
"no_helmet", "helmet"));
private final Map<String, double[]> colors;
public ODConfig() {
this.colors = new HashMap<>();
default_names.forEach(name->{
Random random = new Random();
double[] color = {random.nextDouble()*256, random.nextDouble()*256, random.nextDouble()*256};
colors.put(name, color);
});
}
public String getName(int clsId) {
return names.get(clsId);
}
public double[] getColor(int clsId) {
return colors.get(getName(clsId));
}
public double[] getNameColor(String Name){
return colors.get(Name);
}
public double[] getOtherColor(int clsId) {
return colors.get(default_names.get(clsId));
}
}

View File

@ -0,0 +1,56 @@
package com.dite.znpt.domain.entity;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dite.znpt.domain.AuditableEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
/**
* @author huise23
* @date 2025/07/02 20:57
* @Description: 模型配置表实体类
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("model_config")
@ApiModel(value="ModelConfigEntity对象", description="模型配置表")
public class ModelConfigEntity extends AuditableEntity implements Serializable {
@Serial
private static final long serialVersionUID = -73052440757340126L;
@ExcelProperty("模型id")
@ApiModelProperty("模型id")
@TableId(value = "model_id", type = IdType.ASSIGN_ID)
private String modelId;
@ExcelProperty("模型名称")
@ApiModelProperty("模型名称")
@TableField("model_name")
private String modelName;
@ExcelProperty("模型路径")
@ApiModelProperty("模型路径")
@TableField("model_path")
private String modelPath;
@ExcelProperty("模型置信度")
@ApiModelProperty("模型置信度")
@TableField("conf_threshold")
private Float confThreshold;
@ExcelProperty("非极大抑制")
@ApiModelProperty("非极大抑制")
@TableField("nms_threshold")
private Float nmsThreshold;
}

View File

@ -1,24 +1,27 @@
package com.dite.znpt.domain.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.*;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.dite.znpt.domain.AuditableEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.alibaba.excel.annotation.ExcelProperty;
import java.io.Serializable;
/**
* @author huise23
* @date 2025/04/11 23:17
* @Description: 表实体类
* @Description: 部件表实体类
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("part")
@ApiModel(value="PartEntity对象", description="")
@ApiModel(value="PartEntity对象", description="部件")
public class PartEntity extends AuditableEntity implements Serializable {
private static final long serialVersionUID = -53853862365306266L;

View File

@ -0,0 +1,41 @@
package com.dite.znpt.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author huise23
* @date 2025/07/02 20:57
* @Description: 请求实体
*/
@Data
@ApiModel("列表请求实体")
public class ModelConfigListReq implements Serializable {
@Serial
private static final long serialVersionUID = -41204426418525667L;
@ApiModelProperty("查询关键字")
private String keyword;
@ApiModelProperty("Id")
private String modelId;
@ApiModelProperty("模型名称")
private String modelName;
@ApiModelProperty("模型路径")
private String modelPath;
@ApiModelProperty("模型置信度")
private Float confThreshold;
@ApiModelProperty("非极大抑制")
private Float nmsThreshold;
}

View File

@ -0,0 +1,50 @@
package com.dite.znpt.domain.vo;
import com.dite.znpt.util.ValidationGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.*;
import java.io.Serial;
import java.io.Serializable;
/**
* @author huise23
* @date 2025/07/02 20:57
* @Description: 模型配置表请求类
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="ModelConfig请求对象", description="模型配置表")
public class ModelConfigReq implements Serializable {
@Serial
private static final long serialVersionUID = 930798215980875267L;
@ApiModelProperty("模型id")
private String modelId;
@NotBlank(groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型名称不能为空")
@Size(max = 50, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型名称长度不能超过50")
@ApiModelProperty("模型名称")
private String modelName;
@NotEmpty(groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型附件不能为空")
@ApiModelProperty("模型附件id")
private String attachId;
@NotNull(groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型置信度不能为空")
@Min(value = 0, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型置信度只能在0-100之间")
@Max(value = 100, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型置信度只能在0-100之间")
@ApiModelProperty("模型置信度")
private Float confThreshold;
@NotNull(groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "nms不能为空")
@Min(value = 0, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "nms只能在0-1之间")
@Max(value = 1, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "nms只能在0-1之间")
@ApiModelProperty("nms")
private Float nmsThreshold;
}

View File

@ -0,0 +1,18 @@
package com.dite.znpt.domain.vo;
import com.dite.znpt.domain.entity.ModelConfigEntity;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author huise23
* @date 2025/07/02 20:57
* @Description: 响应实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("响应实体")
public class ModelConfigResp extends ModelConfigEntity {
}

View File

@ -1,21 +1,13 @@
package com.dite.znpt.domain.vo;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.io.Serial;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.*;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.alibaba.excel.annotation.ExcelProperty;
import com.dite.znpt.util.ValidationGroup;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;
/**
* @author huise23
* @date 2025/06/27 14:21
@ -60,11 +52,11 @@ public class ProjectTaskImportReq implements Serializable {
@ExcelProperty(value = "实际结束时间")
private LocalDate actualEndDate;
@ExcelProperty(value = "任务状态0未开始1进行中2已结束")
private Integer status;
@ExcelProperty(value = "任务状态(未开始,进行中,已结束)")
private String status;
@ExcelProperty(value = "是否逾期默认未逾期")
private Integer overdueStatus;
@ExcelProperty(value = "是否逾期(已逾期,未逾期)")
private String overdueStatus;
@ExcelProperty(value = "备注")
private String remark;

View File

@ -15,6 +15,7 @@ import java.util.List;
public enum AttachBusinessTypeEnum {
PROJECT_TASK("PROJECT_TASK", "项目任务"),
INSURANCE_FILE("insurance", "保险文件"),
MODEL_FILE("model", "模型文件"),
;
private final String code;
private final String desc;

View File

@ -22,26 +22,37 @@ public enum FilePathEnum {
private final String fileRelativePath;
/**
* 功能描述获取文件绝对路径
* 功能描述获取文件绝对路径前缀
*
* @return {@link String }
* @author cuizhibin
* @date 2025/06/23 14:46
**/
public String getFileAbsolutePath() {
public String getFileAbsolutePathPrefix() {
return SpringUtil.getBean(Environment.class).getProperty("upload.save-path") + fileRelativePath + FileUtil.FILE_SEPARATOR;
}
/**
* 功能描述获取图像下载路径
* 功能描述获取文件绝对路径全路径
*
* @return {@link String }
* @author cuizhibin
* @date 2025/06/23 14:46
**/
public String getFileAbsolutePath(String fileDownPath) {
return FileUtil.normalize(getFileAbsolutePathPrefix() + StrUtil.removePrefix(fileDownPath, urlPath));
}
/**
* 功能描述获取下载路径
*
* @param absolutePath
* @return {@link String }
* @author cuizhibin
* @date 2025/06/06 09:07
**/
public String getImageDownPath(String absolutePath) {
String relativePath = StrUtil.removePrefix(absolutePath, getFileAbsolutePath());
public String getFileDownPath(String absolutePath) {
String relativePath = StrUtil.removePrefix(absolutePath, getFileAbsolutePathPrefix());
return StrUtil.replace(urlPath.concat(relativePath), FileUtil.FILE_SEPARATOR, StrUtil.SLASH);
}

View File

@ -0,0 +1,18 @@
package com.dite.znpt.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dite.znpt.domain.entity.ModelConfigEntity;
import com.dite.znpt.domain.vo.ModelConfigListReq;
import com.dite.znpt.domain.vo.ModelConfigResp;
import java.util.List;
/**
* @author huise23
* @date 2025/07/02 20:57
* @Description: 模型配置表数据库访问层
*/
public interface ModelConfigMapper extends BaseMapper<ModelConfigEntity> {
List<ModelConfigResp> queryBySelective(ModelConfigListReq modelConfigReq);
}

View File

@ -4,15 +4,13 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dite.znpt.domain.entity.PartEntity;
import com.dite.znpt.domain.vo.PartListReq;
import com.dite.znpt.domain.vo.PartListResp;
import com.dite.znpt.domain.vo.PartResp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author huise23
* @date 2025/04/11 23:17
* @Description: 表数据库访问层
* @Description: 部件表数据库访问层
*/
public interface PartMapper extends BaseMapper<PartEntity> {
List<PartListResp> queryBySelective(PartListReq partReq);

View File

@ -7,7 +7,6 @@ import com.dite.znpt.enums.AttachBusinessTypeEnum;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.rmi.ServerException;
import java.util.List;
/**

View File

@ -0,0 +1,65 @@
package com.dite.znpt.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.dite.znpt.domain.entity.ModelConfigEntity;
import com.dite.znpt.domain.vo.ModelConfigListReq;
import com.dite.znpt.domain.vo.ModelConfigReq;
import com.dite.znpt.domain.vo.ModelConfigResp;
import java.util.List;
/**
* @author huise23
* @date 2025/07/02 20:57
* @Description: 模型配置表服务接口
*/
public interface ModelConfigService extends IService<ModelConfigEntity> {
/**
* 功能描述查询列表
*
* @param modelConfigReq
* @return {@link List }<{@link ModelConfigResp }>
* @author huise23
* @date 2025/07/02 20:57
**/
List<ModelConfigResp> selectList(ModelConfigListReq modelConfigReq);
/**
* 功能描述查询单条
*
* @param modelId Id
* @return {@link ModelConfigResp }
* @author huise23
* @date 2025/07/02 20:57
**/
ModelConfigResp selectById(String modelId);
/**
* 功能描述新增
*
* @param modelConfigReq
* @author huise23
* @date 2025/07/02 20:57
**/
void saveData(ModelConfigReq modelConfigReq);
/**
* 功能描述更新
*
* @param modelConfigReq
* @author huise23
* @date 2025/07/02 20:57
**/
void updateData(ModelConfigReq modelConfigReq);
/**
* 功能描述删除
*
* @param modelId Id
* @author huise23
* @date 2025/07/02 20:57
**/
void deleteById(String modelId);
}

View File

@ -13,7 +13,7 @@ import java.util.List;
/**
* @author huise23
* @date 2025/04/11 23:17
* @Description: 表服务接口
* @Description: 部件表服务接口
*/
public interface PartService extends IService<PartEntity> {

View File

@ -10,17 +10,15 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dite.znpt.constant.Constants;
import com.dite.znpt.constant.Message;
import com.dite.znpt.domain.entity.AttachInfoEntity;
import com.dite.znpt.domain.entity.InsuranceInfoEntity;
import com.dite.znpt.domain.vo.AttachInfoReq;
import com.dite.znpt.enums.AttachBusinessTypeEnum;
import com.dite.znpt.enums.FilePathEnum;
import com.dite.znpt.service.AttachInfoService;
import com.dite.znpt.mapper.FileInfoMapper;
import com.dite.znpt.service.AttachInfoService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import com.dite.znpt.util.PageUtil;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -69,7 +67,7 @@ public class AttachInfoServiceImpl extends ServiceImpl<FileInfoMapper, AttachInf
**/
@Override
public List<String> saveData(String businessType, AttachInfoReq infoReq, MultipartFile[] files) {
String temPathPrefix = FilePathEnum.ATTACH.getFileAbsolutePath().concat(businessType).concat(FileUtil.FILE_SEPARATOR);
String temPathPrefix = FilePathEnum.ATTACH.getFileAbsolutePathPrefix().concat(businessType).concat(FileUtil.FILE_SEPARATOR);
if(StrUtil.isNotBlank(infoReq.getUserDefinedPath())){
temPathPrefix = temPathPrefix.concat(infoReq.getUserDefinedPath()).concat(FileUtil.FILE_SEPARATOR);
}
@ -84,7 +82,7 @@ public class AttachInfoServiceImpl extends ServiceImpl<FileInfoMapper, AttachInf
String path = temPathPrefix + file.getOriginalFilename();
FileUtil.writeBytes(file.getBytes(),path);
AttachInfoEntity attachInfo = AttachInfoEntity.builder()
.attachPath(FilePathEnum.ATTACH.getImageDownPath(path))
.attachPath(FilePathEnum.ATTACH.getFileDownPath(path))
.businessType(businessType)
.fileType(infoReq.getFileType())
.remark(infoReq.getRemark())
@ -105,7 +103,7 @@ public class AttachInfoServiceImpl extends ServiceImpl<FileInfoMapper, AttachInf
if(null == attachInfo || !Constants.DEL_FLAG_0.equals(attachInfo.getDelFlag())){
throw new ServerException(Message.ATTACH_INFO_IS_NOT_EXIST);
}
String filePath = FilePathEnum.ATTACH.getFileAbsolutePath().concat(StrUtil.removePrefix(attachInfo.getAttachPath(), FilePathEnum.ATTACH.getUrlPath()));
String filePath = FilePathEnum.ATTACH.getFileAbsolutePathPrefix().concat(StrUtil.removePrefix(attachInfo.getAttachPath(), FilePathEnum.ATTACH.getUrlPath()));
File file = new File(filePath);
// 检查文件是否存在
if (!file.exists()) {

View File

@ -104,7 +104,7 @@ public class AudioFileInfoServiceImpl extends ServiceImpl<AudioFileInfoMapper, A
throw new ServiceException(Message.IMAGE_ID_IS_NOT_EXIST);
}
PartResp partResp = partService.detail(image.getPartId());
String audioFilePrefix = FilePathEnum.AUDIO.getFileAbsolutePath() + partResp.getProjectName().concat(FileUtil.FILE_SEPARATOR).concat(partResp.getTurbineName()).concat(FileUtil.FILE_SEPARATOR);
String audioFilePrefix = FilePathEnum.AUDIO.getFileAbsolutePathPrefix() + partResp.getProjectName().concat(FileUtil.FILE_SEPARATOR).concat(partResp.getTurbineName()).concat(FileUtil.FILE_SEPARATOR);
List<AudioFileInfoEntity> list = new ArrayList<>();
for (MultipartFile file : files) {
AudioFileInfoEntity audio = new AudioFileInfoEntity();
@ -113,7 +113,7 @@ public class AudioFileInfoServiceImpl extends ServiceImpl<AudioFileInfoMapper, A
try {
String path = audioFilePrefix + file.getOriginalFilename();
FileUtil.writeBytes(file.getBytes(),path);
audio.setFilePath(FilePathEnum.AUDIO.getImageDownPath(path));
audio.setFilePath(FilePathEnum.AUDIO.getFileDownPath(path));
list.add(audio);
} catch (Exception e) {
e.printStackTrace();

View File

@ -56,7 +56,7 @@ public class DictServiceImpl extends ServiceImpl<DictMapper, DictEntity> impleme
dictReq.setDictId(dictId);
List<DictResp> list = selectList(dictReq);
return list.isEmpty() ? CollUtil.getFirst(list) : new DictResp();
return CollUtil.isNotEmpty(list) ? CollUtil.getFirst(list) : new DictResp();
}
/**

View File

@ -52,8 +52,8 @@ public class ImageCollectServiceImpl extends ServiceImpl<ImageCollectMapper, Ima
this.save(imageCollect);
String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
List<ImageEntity> imageList = Converts.INSTANCE.toImageEntity(req.getImageList());
String permPathPrefix = FilePathEnum.IMAGE.getFileAbsolutePath().concat(ImageSourceEnum.COLLECT.getCode()).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR).concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
String temPathPrefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePath().concat(ImageSourceEnum.COLLECT.getCode()).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR);
String permPathPrefix = FilePathEnum.IMAGE.getFileAbsolutePathPrefix().concat(ImageSourceEnum.COLLECT.getCode()).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR).concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
String temPathPrefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePathPrefix().concat(ImageSourceEnum.COLLECT.getCode()).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR);
imageList.forEach(image -> {
image.setPartId(partId);
image.setCollectId(imageCollect.getCollectId());
@ -62,7 +62,7 @@ public class ImageCollectServiceImpl extends ServiceImpl<ImageCollectMapper, Ima
if (file.exists()) {
byte[] bytes = FileUtil.readBytes(file);
FileUtil.writeBytes(bytes, path);
String url = FilePathEnum.IMAGE.getUrlPath().concat(StrUtil.removePrefix(path,FilePathEnum.IMAGE.getFileAbsolutePath()).replace(FileUtil.FILE_SEPARATOR, StrUtil.SLASH));
String url = FilePathEnum.IMAGE.getUrlPath().concat(StrUtil.removePrefix(path,FilePathEnum.IMAGE.getFileAbsolutePathPrefix()).replace(FileUtil.FILE_SEPARATOR, StrUtil.SLASH));
image.setImagePath(url);
FileUtil.del(file);
}else {

View File

@ -1,5 +1,4 @@
package com.dite.znpt.service.impl;
import java.util.ArrayList;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
@ -12,16 +11,16 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dite.znpt.constant.Constants;
import com.dite.znpt.constant.Message;
import com.dite.znpt.domain.bo.PartFullInfoBo;
import com.dite.znpt.domain.entity.AudioFileInfoEntity;
import com.dite.znpt.domain.entity.ImageCollectEntity;
import com.dite.znpt.domain.entity.ImageEntity;
import com.dite.znpt.domain.entity.PartEntity;
import com.dite.znpt.domain.page.PageDomain;
import com.dite.znpt.domain.vo.*;
import com.dite.znpt.enums.*;
import com.dite.znpt.enums.FilePathEnum;
import com.dite.znpt.enums.ImageTypeEnum;
import com.dite.znpt.enums.ShootingMethodEnum;
import com.dite.znpt.enums.WeatherEnum;
import com.dite.znpt.exception.ServiceException;
import com.dite.znpt.mapper.ImageMapper;
import com.dite.znpt.service.AudioFileInfoService;
@ -32,7 +31,6 @@ import com.dite.znpt.util.EXIFUtil;
import com.dite.znpt.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -42,9 +40,7 @@ import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URI;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
@ -103,8 +99,8 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, ImageEntity> impl
Map<String, PartEntity> partIdMap= partService.listByIds(partIds).stream().collect(Collectors.toMap(PartEntity::getPartId, Function.identity()));
list.forEach(req -> {
if(partIdMap.containsKey(req.getPartId())){
String path_prefix = FilePathEnum.IMAGE.getFileAbsolutePath().concat(StrUtil.BACKSLASH).concat(req.getImageSource()).concat(StrUtil.BACKSLASH).concat(req.getPartId()).concat(StrUtil.BACKSLASH);
String temp_path_prefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePath().concat(StrUtil.BACKSLASH).concat(req.getImageSource()).concat(StrUtil.BACKSLASH).concat(req.getPartId()).concat(StrUtil.BACKSLASH);
String path_prefix = FilePathEnum.IMAGE.getFileAbsolutePathPrefix().concat(StrUtil.BACKSLASH).concat(req.getImageSource()).concat(StrUtil.BACKSLASH).concat(req.getPartId()).concat(StrUtil.BACKSLASH);
String temp_path_prefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePathPrefix().concat(StrUtil.BACKSLASH).concat(req.getImageSource()).concat(StrUtil.BACKSLASH).concat(req.getPartId()).concat(StrUtil.BACKSLASH);
File file = new File(req.getImagePath());
if(file.exists() && req.getImagePath().contains(temp_path_prefix)){
ImageEntity entity = new ImageEntity();
@ -146,12 +142,12 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, ImageEntity> impl
throw new ServiceException(Message.IMAGE_IS_EMPTY);
}
List<ImageReq> list = new ArrayList<>(files.length);
File temCategory = new File(FilePathEnum.IMAGE_TEMP.getFileAbsolutePath());
File temCategory = new File(FilePathEnum.IMAGE_TEMP.getFileAbsolutePathPrefix());
if (!temCategory.exists()) {
// 创建完整的目录
temCategory.mkdirs();
}
String temPathPrefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePath().concat(imageSource).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR);
String temPathPrefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePathPrefix().concat(imageSource).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR);
for (MultipartFile file : files) {
if (!file.isEmpty()) {
try {
@ -173,7 +169,7 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, ImageEntity> impl
throw new ServiceException(Message.IMAGE_IS_EMPTY);
}
String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
String path_prefix = FilePathEnum.IMAGE.getFileAbsolutePath().concat(imageSource).concat(FileUtil.FILE_SEPARATOR).concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
String path_prefix = FilePathEnum.IMAGE.getFileAbsolutePathPrefix().concat(imageSource).concat(FileUtil.FILE_SEPARATOR).concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
if (Objects.nonNull(imageWorkReq)) {
path_prefix = path_prefix.concat(StrUtil.emptyToDefault(imageWorkReq.getUploadUser(), "默认用户")).concat(FileUtil.FILE_SEPARATOR)
.concat(StrUtil.emptyToDefault(imageWorkReq.getLongitude(), "0"))
@ -188,7 +184,7 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, ImageEntity> impl
for (MultipartFile multipartFile : files) {
String path = path_prefix + multipartFile.getOriginalFilename();
FileUtil.writeBytes(multipartFile.getBytes(),path);
result.add(FilePathEnum.IMAGE.getImageDownPath(path));
result.add(FilePathEnum.IMAGE.getFileDownPath(path));
}
String partId = imageWorkReq.getPartId();
if (StrUtil.isNotEmpty(partId)) {
@ -202,7 +198,7 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, ImageEntity> impl
List<ImageEntity> imageList = new ArrayList<>();
result.forEach(path -> {
ImageEntity imageEntity = new ImageEntity();
String absolutePath = FilePathEnum.IMAGE.getFileAbsolutePath() + StrUtil.removePrefix(path, FilePathEnum.IMAGE.getUrlPath());
String absolutePath = FilePathEnum.IMAGE.getFileAbsolutePathPrefix() + StrUtil.removePrefix(path, FilePathEnum.IMAGE.getUrlPath());
try {
ImageReq imageReq = imageRespBuilder(absolutePath);
BeanUtil.copyProperties(imageReq, imageEntity);
@ -251,7 +247,7 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, ImageEntity> impl
req.setCameraManufacturer(obj.getStr("Make"));
req.setCameraModel(obj.getStr("Model"));
req.setImageName(obj.getStr("File Name"));
req.setImagePath(FilePathEnum.IMAGE_TEMP.getImageDownPath(path));
req.setImagePath(FilePathEnum.IMAGE_TEMP.getFileDownPath(path));
BigDecimal imageSize = new BigDecimal(extractDigit(obj.getStr("File Size"))).divide(new BigDecimal(1024*1024), 4, RoundingMode.HALF_UP);
req.setImageSize(imageSize.toString().concat("M"));
req.setImageWidth(extractDigit(obj.getStr("Image Width")));
@ -308,11 +304,11 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, ImageEntity> impl
@Override
public List<AppImageResp> listAppUploadImages() {
List<String> filePaths = new ArrayList<>();
PathUtil.walkFiles(Path.of(FilePathEnum.IMAGE.getFileAbsolutePath()), new SimpleFileVisitor<>() {
PathUtil.walkFiles(Path.of(FilePathEnum.IMAGE.getFileAbsolutePathPrefix()), new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
if (path.toFile().isFile()) {
String imageDownPath = FilePathEnum.IMAGE.getImageDownPath(path.toFile().getAbsolutePath());
String imageDownPath = FilePathEnum.IMAGE.getFileDownPath(path.toFile().getAbsolutePath());
List<String> split = StrUtil.split(imageDownPath, StrUtil.SLASH, true, true);
// /static/image/source/date
try {
@ -405,7 +401,7 @@ public class ImageServiceImpl extends ServiceImpl<ImageMapper, ImageEntity> impl
List<ImageEntity> newImageList = new ArrayList<>();
partReq.getImagePaths().forEach(path -> {
ImageEntity imageEntity = new ImageEntity();
String absolutePath = FilePathEnum.IMAGE.getFileAbsolutePath() + StrUtil.removePrefix(path, FilePathEnum.IMAGE.getUrlPath());
String absolutePath = FilePathEnum.IMAGE.getFileAbsolutePathPrefix() + StrUtil.removePrefix(path, FilePathEnum.IMAGE.getUrlPath());
try {
ImageReq imageReq = imageRespBuilder(absolutePath);
BeanUtil.copyProperties(imageReq, imageEntity);

View File

@ -0,0 +1,123 @@
package com.dite.znpt.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dite.znpt.config.YoloModelRegistry;
import com.dite.znpt.domain.entity.AttachInfoEntity;
import com.dite.znpt.domain.entity.ModelConfigEntity;
import com.dite.znpt.domain.vo.ModelConfigListReq;
import com.dite.znpt.domain.vo.ModelConfigReq;
import com.dite.znpt.domain.vo.ModelConfigResp;
import com.dite.znpt.enums.AttachBusinessTypeEnum;
import com.dite.znpt.mapper.ModelConfigMapper;
import com.dite.znpt.service.AttachInfoService;
import com.dite.znpt.service.ModelConfigService;
import com.dite.znpt.util.PageUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @author huise23
* @date 2025/07/02 20:57
* @Description: 模型配置表服务实现类
*/
@Service
@RequiredArgsConstructor
public class ModelConfigServiceImpl extends ServiceImpl<ModelConfigMapper, ModelConfigEntity> implements ModelConfigService {
private final YoloModelRegistry modelRegistry;
private final AttachInfoService attachInfoService;
/**
* 功能描述查询列表
*
* @param modelConfigReq 信息
* @return {@link List }<{@link ModelConfigResp }>
* @author huise23
* @date 2025/07/02 20:57
**/
@Override
public List<ModelConfigResp> selectList(ModelConfigListReq modelConfigReq) {
PageUtil.startPage();
List<ModelConfigResp> modelConfigList= this.baseMapper.queryBySelective(modelConfigReq);
modelConfigList.forEach(resp -> {
});
return modelConfigList;
}
/**
* 功能描述查询单条
*
* @param modelId Id
* @return {@link ModelConfigResp }
* @author huise23
* @date 2025/07/02 20:57
**/
@Override
public ModelConfigResp selectById(String modelId) {
ModelConfigListReq modelConfigReq = new ModelConfigListReq();
modelConfigReq.setModelId(modelId);
List<ModelConfigResp> list = selectList(modelConfigReq);
return CollUtil.isNotEmpty(list) ? CollUtil.getFirst(list) : new ModelConfigResp();
}
/**
* 功能描述新增
*
* @param modelConfigReq
* @author huise23
* @date 2025/07/02 20:57
**/
@SneakyThrows
@Override
@Transactional(rollbackFor = Exception.class)
public void saveData(ModelConfigReq modelConfigReq) {
ModelConfigEntity entity = BeanUtil.copyProperties(modelConfigReq, ModelConfigEntity.class);
AttachInfoEntity attachInfo = attachInfoService.getById(modelConfigReq.getAttachId());
entity.setModelPath(attachInfo.getAttachPath());
save(entity);
attachInfoService.updateBusinessIdByAttachIds(entity.getModelId(), ListUtil.of(modelConfigReq.getAttachId()), AttachBusinessTypeEnum.MODEL_FILE);
modelRegistry.loadModel(entity);
}
/**
* 功能描述更新
*
* @param modelConfigReq
* @author huise23
* @date 2025/07/02 20:57
**/
@SneakyThrows
@Override
@Transactional(rollbackFor = Exception.class)
public void updateData(ModelConfigReq modelConfigReq) {
ModelConfigEntity entity = BeanUtil.copyProperties(modelConfigReq, ModelConfigEntity.class);
AttachInfoEntity attachInfo = attachInfoService.getById(modelConfigReq.getAttachId());
entity.setModelPath(attachInfo.getAttachPath());
attachInfoService.updateBusinessIdByAttachIds(entity.getModelId(), ListUtil.of(modelConfigReq.getAttachId()), AttachBusinessTypeEnum.MODEL_FILE);
updateById(entity);
modelRegistry.reloadModel(entity);
}
/**
* 功能描述删除
*
* @param modelId Id
* @author huise23
* @date 2025/07/02 20:57
**/
@Override
public void deleteById(String modelId) {
modelRegistry.unloadModel(modelId);
removeById(modelId);
}
}

View File

@ -0,0 +1,147 @@
package com.dite.znpt.service.impl;
import ai.onnxruntime.OnnxTensor;
import ai.onnxruntime.OrtEnvironment;
import ai.onnxruntime.OrtException;
import ai.onnxruntime.OrtSession;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.dite.znpt.config.YoloModelRegistry;
import com.dite.znpt.domain.bo.Detection;
import com.dite.znpt.domain.bo.Letterbox;
import com.dite.znpt.domain.bo.ODConfig;
import com.dite.znpt.domain.entity.ModelConfigEntity;
import com.dite.znpt.util.ModelUtil;
import lombok.extern.slf4j.Slf4j;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.nio.FloatBuffer;
import java.util.*;
@Slf4j
@Service
public class MultiModelYoloService {
@Autowired
private YoloModelRegistry registry;
public List<Detection> detect(String modelId, String inputFile, String outputFile, Float confThreshold) throws OrtException {
OrtSession session = registry.getSession(modelId);
YoloModelRegistry.ModelMetadata meta = registry.getMetadata(modelId);
OrtEnvironment environment = registry.getEnvironment();
ModelConfigEntity modelConfig = registry.getModelConfig(modelId);
confThreshold = (Objects.isNull(confThreshold) ? modelConfig.getConfThreshold() : confThreshold) / 100;
Mat img = Imgcodecs.imread(inputFile);
Mat image = img.clone();
Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2RGB);
// 在这里先定义下框的粗细字的大小字的类型字的颜色(按比例设置大小粗细比较好一些)
int minDwDh = Math.min(img.width(), img.height());
int thickness = minDwDh/ ODConfig.lineThicknessRatio;
long start_time = System.currentTimeMillis();
// 更改 image 尺寸
Letterbox letterbox = new Letterbox();
image = letterbox.letterbox(image);
double ratio = letterbox.getRatio();
double dw = letterbox.getDw();
double dh = letterbox.getDh();
int rows = letterbox.getHeight();
int cols = letterbox.getWidth();
int channels = image.channels();
// 将Mat对象的像素值赋值给Float[]对象
float[] pixels = new float[channels * rows * cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
double[] pixel = image.get(j,i);
for (int k = 0; k < channels; k++) {
// 这样设置相当于同时做了image.transpose((2, 0, 1))操作
pixels[rows*cols*k+j*cols+i] = (float) pixel[k]/255.0f;
}
}
}
// 创建OnnxTensor对象
long[] shape = { 1L, (long)channels, (long)rows, (long)cols };
OnnxTensor tensor = OnnxTensor.createTensor(environment, FloatBuffer.wrap(pixels), shape);
HashMap<String, OnnxTensor> stringOnnxTensorHashMap = new HashMap<>();
stringOnnxTensorHashMap.put(session.getInputInfo().keySet().iterator().next(), tensor);
// 运行推理
OrtSession.Result output = session.run(stringOnnxTensorHashMap);
float[][] outputData = ((float[][][])output.get(0).getValue())[0];
outputData = ModelUtil.transposeMatrix(outputData);
Map<Integer, List<float[]>> class2Bbox = new HashMap<>();
for (float[] bbox : outputData) {
float[] conditionalProbabilities = Arrays.copyOfRange(bbox, 4, bbox.length);
int label = ModelUtil.argmax(conditionalProbabilities);
float conf = conditionalProbabilities[label];
if (conf < confThreshold) continue;
bbox[4] = conf;
// xywh to (x1, y1, x2, y2)
ModelUtil.xywh2xyxy(bbox);
// skip invalid predictions
if (bbox[0] >= bbox[2] || bbox[1] >= bbox[3]) continue;
class2Bbox.putIfAbsent(label, new ArrayList<>());
class2Bbox.get(label).add(bbox);
}
List<Detection> detections = new ArrayList<>();
for (Map.Entry<Integer, List<float[]>> entry : class2Bbox.entrySet()) {
int label = entry.getKey();
List<float[]> bboxes = entry.getValue();
bboxes = ModelUtil.nonMaxSuppression(bboxes, modelConfig.getNmsThreshold());
for (float[] bbox : bboxes) {
String labelString = meta.getLabels()[label];
detections.add(new Detection(labelString,entry.getKey(), Arrays.copyOfRange(bbox, 0, 4), bbox[4]));
}
}
// 打印检测结果并将图片写入指定目录
for (Detection detection : detections) {
float[] bbox = detection.getBbox();
log.info(detection.toString());
// 画框
Point topLeft = new Point((bbox[0]-dw)/ratio, (bbox[1]-dh)/ratio);
Point bottomRight = new Point((bbox[2]-dw)/ratio, (bbox[3]-dh)/ratio);
Scalar color = new Scalar(meta.getColors().get(detection.getClsId()));
Imgproc.rectangle(img, topLeft, bottomRight, color, thickness);
// 框上写文字
Point boxNameLoc = new Point((bbox[0]-dw)/ratio, (bbox[1]-dh-2)/ratio-3);
Imgproc.putText(img, detection.getLabel(), boxNameLoc, Imgproc.FONT_HERSHEY_SIMPLEX, 2.5, color, thickness);
}
log.info("检测{},发现{}处问题,耗时:{} ms.", inputFile, detections.size(), (System.currentTimeMillis() - start_time));
// 保存图像到输出目录
Imgcodecs.imwrite(outputFile, img);
return detections;
}
public void runFolderDetection(String modelId, String inputFolder, String outputFolder, Float confThreshold) throws Exception {
List<File> fileList = FileUtil.loopFiles(inputFolder, file -> {
String extName = FileUtil.extName(file);
return StrUtil.equalsAnyIgnoreCase(extName, "jpg", "png");
});
for (File file : fileList) {
List<Detection> detections = detect(modelId, file.getAbsolutePath(), outputFolder+ FileUtil.FILE_SEPARATOR+FileUtil.getName(file), confThreshold);
}
}
}

View File

@ -17,7 +17,6 @@ import com.dite.znpt.domain.vo.PartResp;
import com.dite.znpt.enums.PartTypeEnum;
import com.dite.znpt.exception.ServiceException;
import com.dite.znpt.mapper.PartMapper;
import com.dite.znpt.mapper.TurbineMapper;
import com.dite.znpt.service.PartService;
import com.dite.znpt.service.ProjectService;
import com.dite.znpt.service.TurbineService;
@ -35,7 +34,7 @@ import java.util.stream.Collectors;
/**
* @author huise23
* @date 2025/04/11 23:17
* @Description: 表服务实现类
* @Description: 部件表服务实现类
*/
@Service
@RequiredArgsConstructor

View File

@ -1,8 +1,8 @@
package com.dite.znpt.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -10,20 +10,19 @@ import com.dite.znpt.constant.Message;
import com.dite.znpt.domain.entity.AttachInfoEntity;
import com.dite.znpt.domain.entity.ProjectTaskEntity;
import com.dite.znpt.domain.vo.ProjectTaskListReq;
import com.dite.znpt.domain.vo.ProjectTaskResp;
import com.dite.znpt.domain.vo.ProjectTaskReq;
import com.dite.znpt.domain.vo.ProjectTaskResp;
import com.dite.znpt.domain.vo.ProjectTaskStartReq;
import com.dite.znpt.enums.AttachBusinessTypeEnum;
import com.dite.znpt.enums.ProjectTaskStateEnum;
import com.dite.znpt.exception.ServiceException;
import com.dite.znpt.mapper.ProjectTaskGroupMapper;
import com.dite.znpt.mapper.ProjectTaskMapper;
import com.dite.znpt.service.AttachInfoService;
import com.dite.znpt.service.ProjectTaskService;
import com.dite.znpt.mapper.ProjectTaskMapper;
import org.springframework.stereotype.Service;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import com.dite.znpt.util.PageUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
@ -82,7 +81,7 @@ public class ProjectTaskServiceImpl extends ServiceImpl<ProjectTaskMapper, Proje
projectTaskReq.setTaskId(taskId);
List<ProjectTaskResp> list = selectList(projectTaskReq);
return list.isEmpty() ? CollUtil.getFirst(list) : new ProjectTaskResp();
return CollUtil.isNotEmpty(list) ? CollUtil.getFirst(list) : new ProjectTaskResp();
}
/**

View File

@ -1,10 +1,10 @@
package com.dite.znpt.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dite.znpt.constant.Constants;
import com.dite.znpt.constant.Message;
import com.dite.znpt.domain.entity.VideoFileInfoEntity;
import com.dite.znpt.domain.vo.VideoFileInfoListReq;
@ -12,15 +12,13 @@ import com.dite.znpt.domain.vo.VideoFileInfoReq;
import com.dite.znpt.domain.vo.VideoFileInfoResp;
import com.dite.znpt.enums.FilePathEnum;
import com.dite.znpt.exception.ServiceException;
import com.dite.znpt.mapper.VideoFileInfoMapper;
import com.dite.znpt.service.PartService;
import com.dite.znpt.service.VideoFileInfoService;
import com.dite.znpt.mapper.VideoFileInfoMapper;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import com.dite.znpt.util.PageUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -120,7 +118,7 @@ public class VideoFileInfoServiceImpl extends ServiceImpl<VideoFileInfoMapper, V
throw new ServiceException(Message.IMAGE_IS_EMPTY);
}
String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
String path_prefix = FilePathEnum.VIDEO.getFileAbsolutePath().concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
String path_prefix = FilePathEnum.VIDEO.getFileAbsolutePathPrefix().concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
if (!FileUtil.exist(path_prefix)) {
FileUtil.mkdir(path_prefix);
}
@ -129,7 +127,7 @@ public class VideoFileInfoServiceImpl extends ServiceImpl<VideoFileInfoMapper, V
VideoFileInfoEntity info = BeanUtil.copyProperties(infoReq, VideoFileInfoEntity.class);
String path = path_prefix + multipartFile.getOriginalFilename();
FileUtil.writeBytes(multipartFile.getBytes(), path);
info.setFilePath(FilePathEnum.VIDEO.getUrlPath() + StrUtil.removePrefix(path, FilePathEnum.VIDEO.getFileAbsolutePath()).replace(FileUtil.FILE_SEPARATOR, StrUtil.SLASH));
info.setFilePath(FilePathEnum.VIDEO.getUrlPath() + StrUtil.removePrefix(path, FilePathEnum.VIDEO.getFileAbsolutePathPrefix()).replace(FileUtil.FILE_SEPARATOR, StrUtil.SLASH));
result.add(info);
}
baseMapper.insert(result);

View File

@ -0,0 +1,74 @@
package com.dite.znpt.util;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class ModelUtil {
public static void xywh2xyxy(float[] bbox) {
float x = bbox[0];
float y = bbox[1];
float w = bbox[2];
float h = bbox[3];
bbox[0] = x - w * 0.5f;
bbox[1] = y - h * 0.5f;
bbox[2] = x + w * 0.5f;
bbox[3] = y + h * 0.5f;
}
public static float[][] transposeMatrix(float[][] m) {
float[][] temp = new float[m[0].length][m.length];
for (int i = 0; i < m.length; i++)
for (int j = 0; j < m[0].length; j++)
temp[j][i] = m[i][j];
return temp;
}
//返回最大值的索引
public static int argmax(float[] a) {
float re = -Float.MAX_VALUE;
int arg = -1;
for (int i = 0; i < a.length; i++) {
if (a[i] >= re) {
re = a[i];
arg = i;
}
}
return arg;
}
public static List<float[]> nonMaxSuppression(List<float[]> bboxes, float iouThreshold) {
List<float[]> bestBboxes = new ArrayList<>();
bboxes.sort(Comparator.comparing(a -> a[4]));
while (!bboxes.isEmpty()) {
float[] bestBbox = bboxes.remove(bboxes.size() - 1);
bestBboxes.add(bestBbox);
bboxes = bboxes.stream().filter(a -> computeIOU(a, bestBbox) < iouThreshold).collect(Collectors.toList());
}
return bestBboxes;
}
public static float computeIOU(float[] box1, float[] box2) {
float area1 = (box1[2] - box1[0]) * (box1[3] - box1[1]);
float area2 = (box2[2] - box2[0]) * (box2[3] - box2[1]);
float left = Math.max(box1[0], box2[0]);
float top = Math.max(box1[1], box2[1]);
float right = Math.min(box1[2], box2[2]);
float bottom = Math.min(box1[3], box2[3]);
float interArea = Math.max(right - left, 0) * Math.max(bottom - top, 0);
float unionArea = area1 + area2 - interArea;
return Math.max(interArea / unionArea, 1e-8f);
}
}

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dite.znpt.mapper.ModelConfigMapper">
<sql id="Base_Column_List">
a.model_id, a.model_name, a.model_path, a.conf_threshold,
a.nms_threshold, a.update_by, a.create_time, a.create_by,
a.update_time
</sql>
<select id="queryBySelective" resultType="com.dite.znpt.domain.vo.ModelConfigResp">
select
<include refid="Base_Column_List"/>
from model_config a
<where>
<if test="modelId != null and modelId != ''">
and a.model_id = #{modelId}
</if>
<if test="modelName != null and modelName != ''">
and a.model_name like concat ('%', #{modelName}, '%')
</if>
<if test="modelPath != null and modelPath != ''">
and a.model_path like concat ('%', #{modelPath}, '%')
</if>
<if test="confThreshold != null">
and a.conf_threshold &gt;= #{confThreshold}
</if>
<if test="nmsThreshold != null">
and a.nms_threshold &gt;= #{nmsThreshold}
</if>
</where>
</select>
</mapper>

View File

@ -0,0 +1,83 @@
package com.dite.znpt.web.controller;
import com.dite.znpt.config.YoloModelRegistry;
import com.dite.znpt.domain.PageResult;
import com.dite.znpt.domain.Result;
import com.dite.znpt.domain.vo.ModelConfigListReq;
import com.dite.znpt.domain.vo.ModelConfigReq;
import com.dite.znpt.domain.vo.ModelConfigResp;
import com.dite.znpt.service.ModelConfigService;
import com.dite.znpt.service.impl.MultiModelYoloService;
import com.dite.znpt.util.ValidationGroup;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* @author huise23
* @date 2025/07/02 21:21
*/
@Api(tags = "模型配置")
@RestController
@RequestMapping("/model-config")
public class ModelConfigController {
@Resource
private ModelConfigService modelConfigService;
@Resource
private YoloModelRegistry registry;
@Autowired
private MultiModelYoloService multiModelYoloService;
@ApiOperation(value = "获取列表", httpMethod = "GET")
@GetMapping("/list")
public PageResult<ModelConfigResp> list(ModelConfigListReq modelConfigReq) {
return PageResult.ok(modelConfigService.selectList(modelConfigReq));
}
@ApiOperation(value = "根据Id获取详细信息", httpMethod = "GET")
@GetMapping("/{modelId}")
public Result<ModelConfigResp> getInfo(@PathVariable String modelId) {
return Result.ok(modelConfigService.selectById(modelId));
}
@ApiOperation(value = "新增", httpMethod = "POST")
@PostMapping
public Result<Object> add(@Validated(ValidationGroup.Insert.class) @RequestBody ModelConfigReq modelConfigReq) {
modelConfigService.saveData(modelConfigReq);
return Result.ok();
}
@ApiOperation(value = "修改", httpMethod = "PUT")
@PutMapping
public Result<Object> edit(@Validated(ValidationGroup.Update.class) @RequestBody ModelConfigReq modelConfigReq) {
modelConfigService.updateData(modelConfigReq);
return Result.ok();
}
@ApiOperation(value = "删除", httpMethod = "DELETE")
@DeleteMapping("/{modelId}")
public Result<Object> remove(@PathVariable String modelId) {
modelConfigService.deleteById(modelId);
return Result.ok();
}
@PostMapping("/run")
public ResponseEntity<String> runBatch(@RequestParam("modelId") String modelId,
@RequestParam("inputDir") String inputDir,
@RequestParam("outputDir") String outputDir,
@RequestParam("confThreshold") Float confThreshold) {
try {
multiModelYoloService.runFolderDetection(modelId, inputDir, outputDir, confThreshold);
return ResponseEntity.ok("批量推理完成");
} catch (Exception e) {
return ResponseEntity.status(500).body("处理失败:" + e.getMessage());
}
}
}