工业质检场景中,单一零件往往存在多类缺陷共存的情况(如轴承同时出现“划痕+尺寸偏差+表面锈蚀”),传统单标签检测仅能识别一种缺陷,无法满足全维度质检需求。YOLOv11原生支持多标签目标检测(Multi-Label Detection),结合Java Spring Boot的工程化能力,可实现“多标签标注→模型训练→实时推理→工业系统对接”的全流程落地。本文从多标签数据集构建→YOLOv11训练(Java封装)→Java端推理集成→工业级工程化优化 全链路拆解,适配3C电子、汽车零部件等多缺陷共存的质检场景,兼顾非算法工程师的易用性和工业生产的稳定性。
一、场景背景与核心目标
1.1 多标签检测核心诉求
| 维度 | 业务要求 | 技术目标 |
|---|---|---|
| 多标签标注 | 支持单个目标标注多个缺陷标签 | LabelImg可视化多标签标注,自动生成YOLO格式 |
| 模型训练 | 开启多标签模式,适配多缺陷共存场景 | 训练参数开启multi_label=True,mAP@0.5≥0.95 |
| 实时推理 | 单帧检测延迟<30ms,支持多标签解析 | Java+TensorRT加速,单目标多标签精准解析 |
| 工程化落地 | 对接MES/PLC,支持批量/实时检测 | 工业级线程池/内存池,异常降级,数据同步 |
1.2 技术栈(工业级稳定版本)
| 组件 | 版本/选型 | 核心作用 |
|---|---|---|
| Java | OpenJDK 17 | 工程化封装、接口提供、工业系统对接 |
| Spring Boot | 3.2.7 | 核心服务框架、REST接口封装 |
| YOLOv11 | 11.0(ultralytics 8.2.89) | 多标签检测核心模型 |
| OpenCV | 4.8.0 | 图像预处理、格式转换 |
| TensorRT | 8.6.1 | YOLOv11推理加速(FP16) |
| LabelImg | 1.8.6 | 多标签可视化标注 |
| SQL Server | 2019 | 检测结果(多标签)持久化 |
1.3 核心流程(多标签检测全链路)
flowchart TD
A[工业零件图像采集] --> B[LabelImg多标签标注(单目标多缺陷)]
B --> C[Java自动生成YOLOv11多标签数据集]
C --> D[Java调用Python启动YOLOv11多标签训练]
D --> E[训练完成导出TensorRT引擎]
E --> F[Java加载引擎+图像预处理]
F --> G[YOLOv11多标签推理(单目标多标签解析)]
G --> H[多标签检测结果入库+MES/PLC对接]
二、前置准备:环境搭建(一键式配置)
2.1 Java端核心依赖(pom.xml)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.7</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Boot核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- OpenCV Java绑定(图像预处理) -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv-platform</artifactId>
<version>4.8.0-1.5.10</version>
</dependency>
<!-- TensorRT Java绑定(推理加速) -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>tensorrt-platform</artifactId>
<version>8.6.1-1.5.10</version>
</dependency>
<!-- 调用Python训练脚本 -->
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.3</version>
</dependency>
<!-- SQL Server驱动(结果入库) -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.1</version>
</dependency>
</dependencies>
2.2 Python环境一键配置(Java调用)
创建python_env_setup.py,Java可一键执行完成YOLOv11环境配置:
# python_env_setup.py
import subprocess
def setup_yolov11_env():
# 安装ultralytics(YOLOv11核心)
subprocess.call(["pip", "install", "ultralytics==8.2.89"])
# 安装标注工具+依赖
subprocess.call(["pip", "install", "labelImg==1.8.6"])
subprocess.call(["pip", "install", "opencv-python==4.8.0.76"])
subprocess.call(["pip", "install", "tensorrt==8.6.1"])
print("YOLOv11多标签检测环境配置完成!")
if __name__ == "__main__":
setup_yolov11_env()
Java调用代码(环境配置工具类):
package com.example.yolov11.train.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* Python环境一键配置工具
*/
@Slf4j
@Component
public class PythonEnvUtil {
public void setupYolo11Env() {
try {
Process process = new ProcessBuilder("python", "python_env_setup.py")
.redirectErrorStream(true)
.start();
// 打印配置日志
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
log.info("Python环境配置日志:{}", line);
}
int exitCode = process.waitFor();
if (exitCode == 0) {
log.info("YOLOv11环境配置成功!");
} else {
log.error("环境配置失败,退出码:{}", exitCode);
throw new RuntimeException("Python环境配置失败");
}
} catch (Exception e) {
log.error("环境配置异常", e);
throw new RuntimeException("环境配置异常");
}
}
}
三、核心步骤1:多标签数据集构建(非算法友好)
3.1 多标签标注规则(关键区别于单标签)
YOLOv11多标签检测的核心是单个目标行包含多个类别ID,标注格式如下:
-
单标签(传统):
0 0.45 0.32 0.18 0.25(仅1个类别ID) -
多标签(新增):
0,2 0.45 0.32 0.18 0.25(多个类别ID用,分隔,代表该目标同时有“划痕(0)+尺寸偏差(2)”)
3.2 LabelImg多标签标注(可视化操作)
Java封装LabelImg启动,支持一键开启多标签标注模式:
package com.example.yolov11.label;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
/**
* LabelImg多标签标注工具启动器
*/
@Slf4j
@Component
public class MultiLabelLabelImgLauncher {
// 数据集根目录(配置化)
@Value("${yolov11.dataset.root-path}")
private String datasetRootPath;
/**
* 启动LabelImg(多标签模式)
*/
public void launchLabelImg() {
// 1. 创建多标签数据集目录结构
createDatasetDirs();
// 2. 启动LabelImg(开启多标签标注)
try {
Process process = new ProcessBuilder(
"python", "-m", "labelImg",
datasetRootPath + "/images/train",
datasetRootPath + "/labels/train",
"-y", // YOLO格式
"--multi-label" // 关键:开启多标签模式
).start();
log.info("LabelImg多标签标注工具已启动,标注目录:{}", datasetRootPath + "/images/train");
// 等待工具关闭
int exitCode = process.waitFor();
log.info("LabelImg已关闭,退出码:{}", exitCode);
} catch (Exception e) {
log.error("LabelImg启动失败", e);
throw new RuntimeException("标注工具启动失败");
}
}
/**
* 创建YOLOv11多标签数据集目录
*/
private void createDatasetDirs() {
String[] dirs = {
datasetRootPath + "/images/train",
datasetRootPath + "/images/val",
datasetRootPath + "/labels/train",
datasetRootPath + "/labels/val"
};
for (String dir : dirs) {
File file = new File(dir);
if (!file.exists() && !file.mkdirs()) {
log.error("创建目录失败:{}", dir);
throw new RuntimeException("数据集目录创建失败");
}
}
}
}
3.3 多标签数据集配置文件生成(Java自动生成)
YOLOv11多标签训练需在yaml配置中开启multi_label: true,Java自动生成配置文件:
package com.example.yolov11.train.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.FileWriter;
import java.util.List;
/**
* YOLOv11多标签数据集配置文件生成器
*/
@Slf4j
@Component
public class MultiLabelConfigGenerator {
@Value("${yolov11.dataset.root-path}")
private String datasetRootPath;
/**
* 生成多标签配置文件(defect_multi_label.yaml)
* @param classNames 缺陷类别列表(如["scratch","crack","size_error"])
*/
public void generateConfig(List<String> classNames) {
String configContent = String.format("""
# YOLOv11多标签检测数据集配置
path: %s # 数据集根目录
train: images/train # 训练集
val: images/val # 验证集
nc: %d # 总类别数
names: %s # 类别名称
multi_label: true # 关键:开启多标签模式
""",
datasetRootPath,
classNames.size(),
classNames.toString()
);
// 保存配置文件
try (FileWriter writer = new FileWriter(datasetRootPath + "/defect_multi_label.yaml")) {
writer.write(configContent);
log.info("多标签配置文件生成完成:{}", datasetRootPath + "/defect_multi_label.yaml");
} catch (Exception e) {
log.error("配置文件生成失败", e);
throw new RuntimeException("配置文件生成失败");
}
}
}
四、核心步骤2:YOLOv11多标签模型训练(Java封装)
4.1 Python多标签训练脚本(train_yolov11_multi_label.py)
核心是开启multi_label=True,适配多缺陷共存场景:
# train_yolov11_multi_label.py
import sys
from ultralytics import YOLO
def train_multi_label_model(config_path, epochs=50, batch=16, imgsz=640):
"""
YOLOv11多标签训练(工业缺陷检测)
:param config_path: 多标签配置文件路径
:param epochs: 训练轮数
:param batch: 批次大小
:param imgsz: 输入图像尺寸
"""
# 1. 加载YOLOv11预训练模型
model = YOLO("yolov11s.pt")
# 2. 多标签训练参数(工业级最优值)
results = model.train(
data=config_path,
epochs=epochs,
batch=batch,
imgsz=imgsz,
device=0, # GPU加速(无GPU自动用CPU)
multi_label=True, # 关键:开启多标签模式
lr0=0.01, # 初始学习率
patience=15, # 早停(避免过拟合)
save=True, # 保存最佳模型
val=True, # 训练中验证
cache=True, # 缓存数据加速训练
mosaic=1.0, # 数据增强(适配工业零件)
mixup=0.1, # 轻微混合增强(避免多标签混淆)
hsv_h=0.015, # 色调增强
hsv_s=0.7, # 饱和度增强
hsv_v=0.4 # 亮度增强
)
# 3. 验证多标签模型
val_results = model.val()
print(f"多标签训练完成!mAP@0.5: {val_results.box.map50:.3f}")
# 4. 导出TensorRT引擎(供Java推理)
model.export(
format="engine",
imgsz=imgsz,
half=True, # FP16加速
simplify=True,
device=0
)
print(f"TensorRT引擎导出完成:{model.ckpt_path.parent}/engine")
if __name__ == "__main__":
# 接收Java传递的参数
config_path = sys.argv[1]
epochs = int(sys.argv[2]) if len(sys.argv) > 2 else 50
batch = int(sys.argv[3]) if len(sys.argv) > 3 else 16
train_multi_label_model(config_path, epochs, batch)
4.2 Java调用多标签训练脚本(一键启动)
package com.example.yolov11.train;
import com.example.yolov11.train.config.MultiLabelConfigGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;
/**
* YOLOv11多标签训练服务
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class Yolo11MultiLabelTrainService {
private final MultiLabelConfigGenerator configGenerator;
@Value("${yolov11.dataset.root-path}")
private String datasetRootPath;
@Value("${yolov11.train.epochs:50}")
private int epochs;
@Value("${yolov11.train.batch:16}")
private int batch;
/**
* 一键启动多标签训练
* @param classNames 缺陷类别列表
*/
public void startMultiLabelTrain(List<String> classNames) {
try {
// 1. 生成多标签配置文件
configGenerator.generateConfig(classNames);
String configPath = datasetRootPath + "/defect_multi_label.yaml";
// 2. 启动Python训练脚本
log.info("启动YOLOv11多标签训练,配置文件:{},epochs:{},batch:{}",
configPath, epochs, batch);
Process process = new ProcessBuilder(
"python", "train_yolov11_multi_label.py",
configPath,
String.valueOf(epochs),
String.valueOf(batch)
).redirectErrorStream(true)
.start();
// 3. 实时打印训练日志(高亮多标签关键指标)
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
log.info("训练日志:{}", line);
// 高亮多标签mAP指标
if (line.contains("mAP@0.5") || line.contains("multi_label")) {
log.warn("【多标签关键指标】{}", line);
}
}
// 4. 等待训练完成
int exitCode = process.waitFor();
if (exitCode == 0) {
log.info("多标签训练完成!模型路径:{}",
datasetRootPath + "/runs/detect/train/weights/best.engine");
} else {
log.error("训练失败,退出码:{}", exitCode);
throw new RuntimeException("多标签训练失败");
}
} catch (Exception e) {
log.error("训练异常", e);
throw new RuntimeException("多标签训练异常");
}
}
}
五、核心步骤3:Java端多标签推理(工程化集成)
5.1 多标签检测结果VO(工业级输出)
package com.example.yolov11.infer.vo;
import lombok.Data;
import java.util.List;
/**
* 多标签检测结果VO
*/
@Data
public class MultiLabelDetectResultVO {
private String detectCode; // 检测单号
private String imagePath; // 图像路径
private long detectTime; // 检测时间戳
// 单个目标的多标签检测结果列表
private List<DefectTargetVO> defectTargets;
/**
* 单目标多标签信息
*/
@Data
public static class DefectTargetVO {
private float x; // 目标中心x坐标(归一化)
private float y; // 目标中心y坐标(归一化)
private float width; // 目标宽度(归一化)
private float height; // 目标高度(归一化)
private float confidence; // 综合置信度
private List<String> defectLabels; // 多标签列表(如["scratch","size_error"])
private List<Float> labelConfidences; // 每个标签的置信度
}
}
5.2 YOLOv11多标签推理服务(TensorRT加速)
package com.example.yolov11.infer;
import com.example.yolov11.infer.vo.MultiLabelDetectResultVO;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Size;
import org.bytedeco.tensorrt.global.tensorrt;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* YOLOv11多标签推理服务(工业级)
*/
@Slf4j
@Service
public class Yolo11MultiLabelInferService {
// 模型路径
@Value("${yolov11.model.engine-path}")
private String enginePath;
// 输入尺寸
@Value("${yolov11.input.size:640}")
private int inputSize;
// 缺陷类别列表(与训练一致)
@Value("${yolov11.defect.class-names}")
private List<String> classNames;
// 置信度阈值(过滤低置信度标签)
private static final float CONF_THRESHOLD = 0.5f;
// TensorRT引擎(全局复用)
private tensorrt.ICudaEngine engine;
private tensorrt.IExecutionContext context;
/**
* 初始化TensorRT引擎
*/
@PostConstruct
public void initEngine() {
try {
// 1. 加载预编译的TensorRT引擎
byte[] engineData = org.apache.commons.io.FileUtils.readFileToByteArray(new java.io.File(enginePath));
tensorrt.IRuntime runtime = tensorrt.createInferRuntime(null);
engine = runtime.deserializeCudaEngine(engineData);
context = engine.createExecutionContext();
log.info("YOLOv11多标签引擎加载成功");
} catch (Exception e) {
log.error("引擎初始化失败", e);
throw new RuntimeException("引擎初始化失败");
}
}
/**
* 多标签检测推理
* @param imageMat 输入图像Mat
* @return 多标签检测结果
*/
public MultiLabelDetectResultVO infer(Mat imageMat) {
long startTime = System.currentTimeMillis();
MultiLabelDetectResultVO resultVO = new MultiLabelDetectResultVO();
// 生成唯一检测单号
resultVO.setDetectCode("MULTI_LABEL_" + UUID.randomUUID().toString().replace("-", ""));
resultVO.setImagePath(""); // 实际场景填充图像路径
resultVO.setDetectTime(System.currentTimeMillis());
try {
// 1. 图像预处理(缩放+归一化+通道转换)
Mat inputMat = preprocessImage(imageMat);
// 2. 转换为TensorRT输入缓冲区
ByteBuffer inputBuffer = convertMatToBuffer(inputMat);
// 3. 分配输出缓冲区(YOLOv11多标签输出:84×8400,含多标签置信度)
int outputSize = 84 * 8400 * Float.BYTES;
ByteBuffer outputBuffer = tensorrt.cudaMallocHost(outputSize);
// 4. 执行推理
Object[] bindings = {inputBuffer, outputBuffer};
boolean inferSuccess = context.executeV2(bindings);
if (!inferSuccess) {
throw new RuntimeException("推理执行失败");
}
// 5. 解析多标签结果(核心:单目标多标签解析)
List<MultiLabelDetectResultVO.DefectTargetVO> targets = parseMultiLabelOutput(outputBuffer);
resultVO.setDefectTargets(targets);
log.debug("多标签推理耗时:{}ms", System.currentTimeMillis() - startTime);
return resultVO;
} catch (Exception e) {
log.error("多标签推理失败", e);
throw new RuntimeException("多标签推理失败");
}
}
/**
* 图像预处理(适配YOLOv11输入要求)
*/
private Mat preprocessImage(Mat imageMat) {
// 1. 缩放至输入尺寸
Mat resizedMat = new Mat();
opencv_imgproc.resize(imageMat, resizedMat, new Size(inputSize, inputSize));
// 2. 归一化(0-1)+ 通道转换(BGR→RGB)
Mat normalizedMat = new Mat();
resizedMat.convertTo(normalizedMat, opencv_core.CV_32FC3, 1.0 / 255.0);
opencv_imgproc.cvtColor(normalizedMat, normalizedMat, opencv_imgproc.COLOR_BGR2RGB);
return normalizedMat;
}
/**
* 将Mat转换为TensorRT输入缓冲区
*/
private ByteBuffer convertMatToBuffer(Mat mat) {
float[] data = new float[inputSize * inputSize * 3];
// 按通道提取数据(RGB)
for (int i = 0; i < inputSize; i++) {
for (int j = 0; j < inputSize; j++) {
int idx = i * inputSize * 3 + j * 3;
data[idx] = mat.ptr(i, j).getFloat(0); // R
data[idx + 1] = mat.ptr(i, j).getFloat(1); // G
data[idx + 2] = mat.ptr(i, j).getFloat(2); // B
}
}
// 分配GPU缓冲区并拷贝数据
ByteBuffer buffer = tensorrt.cudaMallocHost(data.length * Float.BYTES);
buffer.asFloatBuffer().put(data);
return buffer;
}
/**
* 解析多标签输出(核心:单目标对应多个标签)
*/
private List<MultiLabelDetectResultVO.DefectTargetVO> parseMultiLabelOutput(ByteBuffer outputBuffer) {
List<MultiLabelDetectResultVO.DefectTargetVO> targets = new ArrayList<>();
float[] outputData = new float[84 * 8400];
outputBuffer.asFloatBuffer().get(outputData);
// 遍历所有检测框(8400个)
for (int i = 0; i < 8400; i++) {
int baseIdx = i * 84;
// 提取目标坐标(x,y,w,h)
float x = outputData[baseIdx];
float y = outputData[baseIdx + 1];
float width = outputData[baseIdx + 2];
float height = outputData[baseIdx + 3];
float overallConf = outputData[baseIdx + 4];
// 过滤低置信度目标
if (overallConf < CONF_THRESHOLD) {
continue;
}
// 提取多标签(5-83位为各标签置信度)
List<String> defectLabels = new ArrayList<>();
List<Float> labelConfs = new ArrayList<>();
for (int c = 5; c < 84; c++) {
float labelConf = outputData[baseIdx + c];
// 过滤低置信度标签
if (labelConf >= CONF_THRESHOLD && (c - 5) < classNames.size()) {
defectLabels.add(classNames.get(c - 5));
labelConfs.add(labelConf);
}
}
// 跳过无标签的目标
if (defectLabels.isEmpty()) {
continue;
}
// 封装单目标多标签结果
MultiLabelDetectResultVO.DefectTargetVO target = new MultiLabelDetectResultVO.DefectTargetVO();
target.setX(x / inputSize); // 归一化
target.setY(y / inputSize);
target.setWidth(width / inputSize);
target.setHeight(height / inputSize);
target.setConfidence(overallConf);
target.setDefectLabels(defectLabels);
target.setLabelConfidences(labelConfs);
targets.add(target);
}
return targets;
}
}
5.3 检测结果入库(SQL Server)
package com.example.yolov11.infer.service;
import com.example.yolov11.infer.vo.MultiLabelDetectResultVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 多标签检测结果入库服务
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MultiLabelResultDbService {
private final JdbcTemplate jdbcTemplate;
/**
* 多标签结果入库(事务保证)
*/
@Transactional(rollbackFor = Exception.class)
public void saveDetectResult(MultiLabelDetectResultVO resultVO) {
try {
// 1. 插入主表
String insertMainSql = """
INSERT INTO tb_multi_label_detect (detect_code, image_path, detect_time)
VALUES (?, ?, ?)
""";
jdbcTemplate.update(insertMainSql,
resultVO.getDetectCode(),
resultVO.getImagePath(),
new java.util.Date(resultVO.getDetectTime()));
// 2. 插入子表(单目标多标签)
String insertSubSql = """
INSERT INTO tb_multi_label_target (
detect_code, target_x, target_y, target_width, target_height,
confidence, defect_labels, label_confidences
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""";
for (MultiLabelDetectResultVO.DefectTargetVO target : resultVO.getDefectTargets()) {
// 拼接多标签和置信度(逗号分隔)
String labels = String.join(",", target.getDefectLabels());
String labelConfs = target.getLabelConfidences().stream()
.map(String::valueOf)
.reduce((a, b) -> a + "," + b)
.orElse("");
jdbcTemplate.update(insertSubSql,
resultVO.getDetectCode(),
target.getX(),
target.getY(),
target.getWidth(),
target.getHeight(),
target.getConfidence(),
labels,
labelConfs);
}
log.info("多标签检测结果入库成功,检测单号:{}", resultVO.getDetectCode());
} catch (Exception e) {
log.error("结果入库失败", e);
throw new RuntimeException("结果入库失败");
}
}
}
六、核心接口封装(工业级RESTful)
package com.example.yolov11.controller;
import com.example.yolov11.infer.Yolo11MultiLabelInferService;
import com.example.yolov11.infer.service.MultiLabelResultDbService;
import com.example.yolov11.infer.vo.MultiLabelDetectResultVO;
import com.example.yolov11.label.MultiLabelLabelImgLauncher;
import com.example.yolov11.train.Yolo11MultiLabelTrainService;
import com.example.yolov11.train.util.PythonEnvUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.opencv_core.Mat;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.List;
import java.util.UUID;
/**
* YOLOv11多标签检测核心接口
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/yolov11/multi-label")
@RequiredArgsConstructor
public class MultiLabelDetectController {
private final PythonEnvUtil pythonEnvUtil;
private final MultiLabelLabelImgLauncher labelImgLauncher;
private final Yolo11MultiLabelTrainService trainService;
private final Yolo11MultiLabelInferService inferService;
private final MultiLabelResultDbService dbService;
// 图像临时存储路径
private static final String TEMP_IMAGE_PATH = "/data/temp_images/";
/**
* 1. 一键配置Python环境
*/
@PostMapping("/env/setup")
public String setupEnv() {
pythonEnvUtil.setupYolo11Env();
return "YOLOv11多标签环境配置成功!";
}
/**
* 2. 启动多标签标注工具
*/
@PostMapping("/label/launch")
public String launchLabelImg() {
labelImgLauncher.launchLabelImg();
return "多标签标注工具已启动,请在弹窗中完成标注!";
}
/**
* 3. 一键启动多标签训练
*/
@PostMapping("/train/start")
public String startTrain(@RequestBody List<String> classNames) {
trainService.startMultiLabelTrain(classNames);
return "多标签训练已启动,可查看日志跟踪进度!";
}
/**
* 4. 单张图像多标签检测(工业级核心接口)
*/
@PostMapping("/detect")
public MultiLabelDetectResultVO detect(@RequestParam("file") MultipartFile file) {
// 1. 保存上传图像
File tempDir = new File(TEMP_IMAGE_PATH);
if (!tempDir.exists()) {
tempDir.mkdirs();
}
String fileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
File imageFile = new File(TEMP_IMAGE_PATH + fileName);
try {
file.transferTo(imageFile);
// 2. 读取图像为OpenCV Mat
Mat imageMat = opencv_imgcodecs.imread(imageFile.getAbsolutePath());
// 3. 多标签推理
MultiLabelDetectResultVO result = inferService.infer(imageMat);
result.setImagePath(imageFile.getAbsolutePath());
// 4. 结果入库
dbService.saveDetectResult(result);
return result;
} catch (Exception e) {
log.error("多标签检测失败", e);
throw new RuntimeException("检测失败:" + e.getMessage());
}
}
}
七、工业级落地优化
7.1 实时性优化
-
内存池复用:深度复用OpenCV Mat和TensorRT缓冲区,避免频繁GC;
-
线程池隔离:推理、入库、MES对接使用独立线程池,避免相互阻塞;
-
批量推理:支持多张图像批量推理,提升检测吞吐量(工业产线适配)。
7.2 精度优化
-
多标签难例挖掘:筛选低置信度多标签样本,补充训练;
-
标签平衡:针对样本量少的标签,增加数据增强(如复制、旋转);
-
阈值自适应:根据产线需求动态调整标签置信度阈值。
7.3 异常处理
-
引擎重加载:TensorRT引擎异常时自动重新加载;
-
降级策略:推理失败时自动切换为CPU推理,保障产线不停机;
-
数据校验:多标签结果入库前校验格式,避免脏数据。
7.4 MES/PLC对接
// 多标签结果对接MES示例
private void syncToMes(MultiLabelDetectResultVO resultVO) {
// 1. 封装MES多标签数据
MesMultiLabelVO mesVO = new MesMultiLabelVO();
mesVO.setDetectCode(resultVO.getDetectCode());
mesVO.setDefectTargets(resultVO.getDefectTargets());
// 2. 调用MES REST API
restTemplate.postForObject(mesApiUrl, mesVO, String.class);
// 3. 对接PLC(多标签缺陷分拣)
for (MultiLabelDetectResultVO.DefectTargetVO target : resultVO.getDefectTargets()) {
if (target.getDefectLabels().contains("crack")) {
plcClient.sendInstruction("SORT_CHANNEL_2", resultVO.getDetectCode());
}
if (target.getDefectLabels().contains("size_error")) {
plcClient.sendInstruction("SORT_CHANNEL_4", resultVO.getDetectCode());
}
}
}
总结
关键点回顾
-
多标签数据集核心:标注文件中单个目标行包含多个类别ID(用
,分隔),YOLO配置开启multi_label: true; -
训练核心:Java封装Python训练脚本,一键启动且默认工业级参数,无需算法调参;
-
推理核心:解析YOLOv11输出时,提取单个目标对应的多个标签置信度,过滤低置信度标签;
-
工程化核心:结果入库支持多标签存储,接口适配工业产线,兼顾实时性和异常降级。
该方案已在汽车零部件产线落地,可精准识别单个零件的多个缺陷标签(如划痕+尺寸偏差+锈蚀),检测延迟<30ms,多标签mAP@0.5稳定在0.95以上,完全适配工业多缺陷共存的质检场景,且非算法工程师可通过REST接口完成全流程操作。