以观书法
108.85M · 2026-02-05
YOLOv26作为新一代轻量化检测模型,相比YOLOv8推理速度提升22%、参数量减少15%,非常适合Java端快速落地。本文针对零基础Java开发者,打造“30分钟闭环”实战教程——从环境搭建、模型准备,到核心调用、结果可视化,全程无复杂深度学习概念,只需Java基础,跟着步骤做就能在30分钟内跑通第一个Java+YOLOv26检测案例,还能直观看到带检测框的可视化结果。
| 环节 | 耗时 | 核心目标 |
|---|---|---|
| 环境搭建 | 10分钟 | 配置JDK/Maven,引入核心依赖 |
| YOLOv26模型准备 | 5分钟 | 获取可直接调用的ONNX格式模型 |
| 核心代码实现 | 10分钟 | 完成预处理、推理、结果解析 |
| 结果可视化 | 5分钟 | 绘制检测框,保存/展示检测结果 |
确保本地已安装以下工具(无则快速安装):
JDK 11+:打开命令行执行 java -version,输出java version "11.0.x"即可;
Maven 3.6+:执行 mvn -v,输出Apache Maven 3.6.x即可。
新手安装提示:Windows可直接下载JDK/Maven压缩包,配置JAVA_HOME/MAVEN_HOME环境变量;Linux执行apt install openjdk-11-jdk maven一键安装。
新建Maven项目(IDEA/Eclipse均可),GroupId填com.yolo.demo,ArtifactId填yolov26-java-demo;
替换pom.xml内容(核心依赖已适配Windows/Linux,直接复制):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 d">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yolo.demo</groupId>
<artifactId>yolov26-java-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 微软仓库(ONNX Runtime依赖) -->
<repositories>
<repository>
<id>microsoft-maven</id>
<url></url>
</repository>
<repository>
<id>aliyun</id>
<url></url>
</repository>
</repositories>
<properties>
<java.version>11</java.version>
<onnxruntime.version>1.18.0</onnxruntime.version>
<javacv.version>1.5.11</javacv.version>
</properties>
<dependencies>
<!-- 核心依赖:ONNX Runtime(Windows x64,Linux替换为linux-x86_64/linux-arm64) -->
<dependency>
<groupId>com.microsoft.onnxruntime</groupId>
<artifactId>onnxruntime</artifactId>
<version>${onnxruntime.version}</version>
<classifier>windows-x86_64</classifier>
</dependency>
<!-- JavaCV:图像预处理+可视化(OpenCV/FFmpeg) -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>${javacv.version}</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv-platform</artifactId>
<version>4.8.0-${javacv.version}</version>
</dependency>
<!-- 工具类:简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
点击IDEA的Load Maven Changes,或执行mvn clean install;
关键适配:若部署到Linux,将onnxruntime的classifier改为linux-x86_64(x86服务器)/linux-arm64(树莓派);
下载慢解决:确保pom.xml已配置阿里云镜像,耐心等待依赖下载完成(首次下载约3-5分钟)。
YOLOv26官方提供预训练模型,需转换为ONNX格式(新手直接用以下简化方式):
临时安装Python(仅用于导出模型,无需深入学习):安装Python 3.8+,执行pip install ultralytics;
新建export_yolov26.py,运行导出ONNX模型:
# export_yolov26.py(仅需运行一次)
from ultralytics import YOLO
# 加载YOLOv26n轻量化模型(适合Java端)
model = YOLO('yolov26n.pt')
# 导出ONNX格式(简化+适配Java)
model.export(format='onnx', imgsz=640, simplify=True, opset=12)
models文件夹,将生成的yolov26n.onnx复制到models目录。确认models/yolov26n.onnx文件存在(大小约4MB),模型准备完成。
确保项目结构如下(新手直接按此创建包和类):
yolov26-java-demo/
├── models/
│ └── yolov26n.onnx
├── src/
│ └── main/
│ └── java/
│ └── com/
│ └── yolo/
│ └── demo/
│ ├── config/YOLOConfig.java // 配置类
│ ├── util/PreprocessUtils.java // 预处理工具
│ ├── engine/YOLOv26Engine.java // 推理引擎
│ ├── util/VisualizeUtils.java // 可视化工具
│ └── DemoApplication.java // 测试入口
// YOLOConfig.java
package com.yolo.demo.config;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
/**
* YOLOv26配置(新手无需修改)
*/
@Getter
public class YOLOConfig {
// 模型路径
public static final String MODEL_PATH = "models/yolov26n.onnx";
// 输入尺寸(与导出模型时的imgsz一致)
public static final int INPUT_WIDTH = 640;
public static final int INPUT_HEIGHT = 640;
// 置信度阈值(过滤低置信度目标)
public static final float CONF_THRESHOLD = 0.35f;
// NMS IOU阈值(去重重复框)
public static final float NMS_THRESHOLD = 0.45f;
// YOLOv26默认类别(80类,新手无需修改)
public static final Map<Integer, String> CLASS_MAP = new HashMap<>();
static {
CLASS_MAP.put(0, "person");
CLASS_MAP.put(1, "bicycle");
CLASS_MAP.put(2, "car");
CLASS_MAP.put(3, "motorcycle");
CLASS_MAP.put(4, "airplane");
CLASS_MAP.put(5, "bus");
CLASS_MAP.put(6, "train");
CLASS_MAP.put(7, "truck");
CLASS_MAP.put(8, "boat");
CLASS_MAP.put(9, "traffic light");
// 其余类别省略,完整80类可直接复制:
}
}
// PreprocessUtils.java
package com.yolo.demo.util;
import com.yolo.demo.config.YOLOConfig;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Size;
import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
/**
* 图像预处理:与YOLOv26要求一致
*/
public class PreprocessUtils {
/**
* 预处理:BGR→RGB + 缩放 + 归一化 + HWC→CHW
*/
public static float[] preprocess(Mat srcImg) {
// 1. BGR转RGB(OpenCV默认BGR)
Mat rgbImg = new Mat();
cvtColor(srcImg, rgbImg, COLOR_BGR2RGB);
// 2. 等比例缩放(640×640)
Mat resizedImg = new Mat();
resize(rgbImg, resizedImg, new Size(YOLOConfig.INPUT_WIDTH, YOLOConfig.INPUT_HEIGHT), 0, 0, INTER_LINEAR);
// 3. 归一化(/255)
Mat floatImg = new Mat();
resizedImg.convertTo(floatImg, CV_32F, 1.0 / 255.0);
// 4. HWC→CHW格式转换
float[] hwcData = new float[YOLOConfig.INPUT_HEIGHT * YOLOConfig.INPUT_WIDTH * 3];
floatImg.get(0, 0, hwcData);
float[] chwData = new float[3 * YOLOConfig.INPUT_HEIGHT * YOLOConfig.INPUT_WIDTH];
for (int c = 0; c < 3; c++) {
for (int h = 0; h < YOLOConfig.INPUT_HEIGHT; h++) {
for (int w = 0; w < YOLOConfig.INPUT_WIDTH; w++) {
int hwcIdx = h * YOLOConfig.INPUT_WIDTH * 3 + w * 3 + c;
int chwIdx = c * YOLOConfig.INPUT_HEIGHT * YOLOConfig.INPUT_WIDTH + h * YOLOConfig.INPUT_WIDTH + w;
chwData[chwIdx] = hwcData[hwcIdx];
}
}
}
// 释放资源
rgbImg.release();
resizedImg.release();
floatImg.release();
return chwData;
}
/**
* 还原目标框到原始图像尺寸
*/
public static int[] restoreBox(float[] box, int srcW, int srcH) {
float x = box[0], y = box[1], w = box[2], h = box[3];
float scaleX = (float) srcW / YOLOConfig.INPUT_WIDTH;
float scaleY = (float) srcH / YOLOConfig.INPUT_HEIGHT;
// 还原坐标(避免超出图像范围)
int x1 = (int) Math.max(0, (x - w / 2) * scaleX);
int y1 = (int) Math.max(0, (y - h / 2) * scaleY);
int x2 = (int) Math.min(srcW, (x + w / 2) * scaleX);
int y2 = (int) Math.min(srcH, (y + h / 2) * scaleY);
return new int[]{x1, y1, x2, y2};
}
}
// YOLOv26Engine.java
package com.yolo.demo.engine;
import ai.onnxruntime.*;
import com.yolo.demo.config.YOLOConfig;
import com.yolo.demo.util.PreprocessUtils;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.opencv.opencv_core.Mat;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* YOLOv26推理核心类
*/
@Slf4j
public class YOLOv26Engine {
private static YOLOv26Engine instance; // 单例模式(新手简化调用)
private final ReentrantLock lock = new ReentrantLock();
private OrtEnvironment env;
private OrtSession session;
// 单例初始化
public static YOLOv26Engine getInstance() {
if (instance == null) {
synchronized (YOLOv26Engine.class) {
if (instance == null) {
instance = new YOLOv26Engine();
instance.initEngine();
}
}
}
return instance;
}
/**
* 初始化模型
*/
private void initEngine() {
lock.lock();
try {
env = OrtEnvironment.getEnvironment();
OrtSession.SessionOptions options = new OrtSession.SessionOptions();
options.setIntraOpNumThreads(2); // 线程数(新手无需修改)
options.setOptimizationLevel(OrtSession.SessionOptions.OptLevel.ALL);
session = env.createSession(YOLOConfig.MODEL_PATH, options);
log.info("YOLOv26模型加载成功!");
} catch (OrtException e) {
log.error("模型加载失败", e);
throw new RuntimeException("模型初始化失败");
} finally {
lock.unlock();
}
}
/**
* 核心检测方法
*/
public List<Map<String, Object>> detect(Mat srcImg) {
lock.lock();
List<Map<String, Object>> resultList = new ArrayList<>();
try {
int srcW = srcImg.cols();
int srcH = srcImg.rows();
// 1. 预处理
float[] inputData = PreprocessUtils.preprocess(srcImg);
long[] inputShape = new long[]{1, 3, YOLOConfig.INPUT_HEIGHT, YOLOConfig.INPUT_WIDTH};
OrtSession.InputTensor inputTensor = OrtSession.InputTensor.createTensor(env, inputData, inputShape);
Map<String, OrtSession.InputTensor> inputs = new HashMap<>();
inputs.put("images", inputTensor);
// 2. 推理
OrtSession.Result ortResult = session.run(inputs);
float[][] output = (float[][]) ortResult.get(0).getValue();
// 3. 解析结果
resultList = parseOutput(output, srcW, srcH);
// 释放资源
inputTensor.close();
ortResult.close();
} catch (Exception e) {
log.error("检测失败", e);
} finally {
lock.unlock();
}
return resultList;
}
/**
* 解析YOLOv26输出结果
*/
private List<Map<String, Object>> parseOutput(float[][] output, int srcW, int srcH) {
List<Map<String, Object>> detectList = new ArrayList<>();
// YOLOv26输出格式:[8400, 84] → 前4列坐标,后80列类别置信度
for (int i = 0; i < 8400; i++) {
float[] boxData = output[i];
// 筛选置信度最高的类别
float maxConf = 0.0f;
int maxClsId = -1;
for (int j = 4; j < 84; j++) {
if (boxData[j] > maxConf) {
maxConf = boxData[j];
maxClsId = j - 4;
}
}
// 过滤低置信度目标
if (maxConf < YOLOConfig.CONF_THRESHOLD || maxClsId == -1) {
continue;
}
// 还原目标框
int[] rect = PreprocessUtils.restoreBox(boxData, srcW, srcH);
// 封装结果
Map<String, Object> obj = new HashMap<>();
obj.put("x1", rect[0]);
obj.put("y1", rect[1]);
obj.put("x2", rect[2]);
obj.put("y2", rect[3]);
obj.put("confidence", maxConf);
obj.put("classId", maxClsId);
obj.put("className", YOLOConfig.CLASS_MAP.getOrDefault(maxClsId, "unknown"));
detectList.add(obj);
}
// NMS去重
return nms(detectList, YOLOConfig.NMS_THRESHOLD);
}
/**
* NMS非极大值抑制(去重重复框)
*/
private List<Map<String, Object>> nms(List<Map<String, Object>> boxes, float iouThreshold) {
boxes.sort((a, b) -> Float.compare((float) b.get("confidence"), (float) a.get("confidence")));
List<Map<String, Object>> result = new ArrayList<>();
while (!boxes.isEmpty()) {
Map<String, Object> maxBox = boxes.remove(0);
result.add(maxBox);
boxes.removeIf(box -> calculateIOU(maxBox, box) > iouThreshold);
}
return result;
}
/**
* 计算IOU(交并比)
*/
private float calculateIOU(Map<String, Object> a, Map<String, Object> b) {
int x1 = (int) a.get("x1"), y1 = (int) a.get("y1"), x2 = (int) a.get("x2"), y2 = (int) a.get("y2");
int bx1 = (int) b.get("x1"), by1 = (int) b.get("y1"), bx2 = (int) b.get("x2"), by2 = (int) b.get("y2");
int interX1 = Math.max(x1, bx1);
int interY1 = Math.max(y1, by1);
int interX2 = Math.min(x2, bx2);
int interY2 = Math.min(y2, by2);
int interArea = Math.max(0, interX2 - interX1) * Math.max(0, interY2 - interY1);
int aArea = (x2 - x1) * (y2 - y1);
int bArea = (bx2 - bx1) * (by2 - by1);
return (float) interArea / (aArea + bArea - interArea);
}
/**
* 释放资源
*/
public void release() {
lock.lock();
try {
if (session != null) session.close();
if (env != null) env.close();
} catch (OrtException e) {
log.error("释放资源失败", e);
} finally {
lock.unlock();
}
}
}
// VisualizeUtils.java
package com.yolo.demo.util;
import org.bytedeco.opencv.opencv_core.*;
import java.util.List;
import java.util.Map;
import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
/**
* 结果可视化:绘制检测框、类别、置信度
*/
public class VisualizeUtils {
/**
* 绘制检测结果
*/
public static Mat drawResult(Mat srcImg, List<Map<String, Object>> detectList) {
// 复制原图(避免修改原图)
Mat drawImg = srcImg.clone();
// 颜色:红色(检测框)、白色(文字)
Scalar boxColor = new Scalar(0, 0, 255, 255);
Scalar textColor = new Scalar(255, 255, 255, 255);
for (Map<String, Object> obj : detectList) {
int x1 = (int) obj.get("x1");
int y1 = (int) obj.get("y1");
int x2 = (int) obj.get("x2");
int y2 = (int) obj.get("y2");
String className = (String) obj.get("className");
float confidence = (float) obj.get("confidence");
// 1. 绘制矩形框
rectangle(drawImg, new Point(x1, y1), new Point(x2, y2), boxColor, 2, LINE_AA, 0);
// 2. 绘制文字背景(半透明)
String text = String.format("%s (%.2f)", className, confidence);
int textSize = 1;
int textThickness = 1;
Size textSizeObj = getTextSize(text, FONT_HERSHEY_SIMPLEX, textSize, textThickness, null);
Rect textRect = new Rect(x1, y1 - 30, textSizeObj.width(), textSizeObj.height() + 10);
rectangle(drawImg, textRect, boxColor, FILLED, LINE_AA, 0);
// 3. 绘制文字
putText(drawImg, text, new Point(x1, y1 - 10), FONT_HERSHEY_SIMPLEX, textSize, textColor, textThickness, LINE_AA, false);
}
return drawImg;
}
}
// DemoApplication.java
package com.yolo.demo;
import com.yolo.demo.engine.YOLOv26Engine;
import com.yolo.demo.util.VisualizeUtils;
import org.bytedeco.opencv.opencv_core.Mat;
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
import java.util.List;
import java.util.Map;
/**
* 测试入口:30分钟上手核心
*/
public class DemoApplication {
public static void main(String[] args) {
// 1. 准备测试图片(新手可将test.jpg放在项目根目录)
String imgPath = "test.jpg"; // 替换为自己的图片路径(如包含人物/车辆的图片)
Mat srcImg = imread(imgPath);
if (srcImg.empty()) {
System.out.println("图片加载失败,请检查路径!");
return;
}
// 2. 初始化YOLOv26引擎
YOLOv26Engine engine = YOLOv26Engine.getInstance();
// 3. 执行检测
long start = System.currentTimeMillis();
List<Map<String, Object>> detectList = engine.detect(srcImg);
long cost = System.currentTimeMillis() - start;
// 4. 打印检测结果
System.out.println("===== 检测结果 =====");
System.out.printf("耗时:%dms,检测到目标数:%d%n", cost, detectList.size());
for (Map<String, Object> obj : detectList) {
System.out.printf("类别:%s,置信度:%.2f,坐标:(%d,%d)-(%d,%d)%n",
obj.get("className"), obj.get("confidence"),
obj.get("x1"), obj.get("y1"), obj.get("x2"), obj.get("y2"));
}
// 5. 可视化并保存结果
Mat resultImg = VisualizeUtils.drawResult(srcImg, detectList);
imwrite("result.jpg", resultImg);
System.out.println("可视化结果已保存为:result.jpg");
// 6. 释放资源
engine.release();
srcImg.release();
resultImg.release();
}
}
在项目根目录放入一张测试图片(命名为test.jpg,建议包含人物、车辆等常见目标);
运行DemoApplication.main();
控制台输出检测结果,项目根目录生成result.jpg(带红色检测框的可视化图片)。
| 问题现象 | 解决方案 |
|---|---|
| 模型加载失败:找不到onnxruntime_jni.dll | 1. 确认pom.xml的classifier与系统匹配(Windows x64/Linux x86_64);2. 重启IDEA,重新下载依赖 |
| 图片加载失败 | 检查test.jpg路径是否正确,确保图片存在且格式为jpg |
| 检测结果为空 | 1. 降低YOLOConfig的CONF_THRESHOLD至0.25;2. 确保测试图片有YOLOv26支持的类别(person/car等) |
| 检测框偏移 | 检查PreprocessUtils的restoreBox方法,确保宽高顺序正确(cols=宽,rows=高) |
环境关键:ONNX Runtime的classifier需匹配操作系统(Windows/Linux/x86/ARM),依赖下载优先用阿里云镜像;
模型适配:YOLOv26的ONNX模型需导出为640×640尺寸,opset=12保证兼容性;
预处理匹配:必须完成BGR→RGB、归一化、HWC→CHW,否则检测结果异常;
可视化核心:通过JavaCV绘制矩形框和文字,新手可直接复用VisualizeUtils;
资源释放:Mat、OrtTensor等原生资源必须手动释放,避免内存泄漏。
本文通过30分钟闭环教程,实现了Java+YOLOv26从环境搭建到可视化的全流程,新手只需跟着步骤复制代码、替换路径,就能快速跑通第一个检测案例。后续可基于此基础,适配自定义数据集、优化推理性能,或对接视频流检测场景。