Java 接 YOLO ONNX:跨语言视频目标检测落地实践
简介本资源面向需要在 Java 项目中落地视频目标检测的开发者提供一套 Java 调用 Python YOLO ONNX 模型的完整方案支持 YOLOv5、YOLOv7、YOLOv8 等主流模型并覆盖 RTSP/RTMP 视频流处理场景。整体架构由 Java 端负责视频流获取、预处理、数据传递、模型调用、后处理与结果展示Python 脚本负责加载 ONNX 模型执行推理并回传识别结果形成清晰的跨语言协作链路。压缩包共 32 个文件约 144.73MB包含 12 个 Java 源码、4 个 ONNX 模型、7 张效果图、2 个 dll 依赖以及 xml、jar、md 等配置与说明文件便于直接运行与二次开发。目前已有 506 人学习下载。读者可据此掌握 resize、normalization、padding 等预处理细节以及置信度过滤、识别框绘制等后处理思路快速搭建可复用的视频目标检测工程。1. Java 接 YOLO ONNX跨语言视频目标检测的落地路径很多做 Java 后端的同学遇到视频目标检测需求时第一反应是「Java 生态里深度学习支持太弱是不是得整个换 Python」。我一开始也这么想直到拆开yolo-onnx-java.zip这个包发现它给出的答案很务实Java 负责它最擅长的视频流获取、帧预处理和结果展示Python 只做一件事——加载 ONNX 模型跑推理两边通过进程调用把数据串起来。这套方案支持 YOLOv5、YOLOv7、YOLOv8 导出的 ONNX 模型覆盖 RTSP/RTMP 视频流场景适合已有 Java 服务、不想重构成微服务架构、又需要快速接入目标检测能力的团队。它不追求极致性能但胜在工程边界清晰、每一层都能单独调试对 Java 工程师来说上手成本远低于硬啃 DJL 或 TensorFlow Java。2. 架构拆解Java 与 Python 的职责边界怎么划2.1 为什么不让 Java 直接推理 ONNXONNX Runtime 本身有 Java API理论上 Java 可以自己加载.onnx模型跑推理不需要 Python 介入。但实际落地时会撞上几个硬问题第一YOLO 系列的后处理NMS、坐标还原、多尺度特征图解码在不同版本间差异很大YOLOv5 和 YOLOv8 的输出层格式完全不同用 Java 重写这些逻辑工作量大且容易出错第二Python 侧的onnxruntime和numpy生态成熟预处理letterbox、归一化和后处理都有现成参考实现改起来快第三模型迭代时通常由算法同学用 Python 导出和验证保持 Python 推理链路可以减少沟通成本。所以这个包的选型逻辑是Java 做「工程活」——拉流、解码、组帧、调进程、画框、推流Python 做「算法活」——加载模型、推理、返回原始检测结果。两边通过标准输入输出或临时文件传递数据耦合度低任何一侧出问题都能单独替换。2.2 数据流转的完整链路整个流程可以拆成六步每一步都有明确的输入输出视频流获取Java 侧用javacv或ffmpeg命令行拉取 RTSP/RTMP 流解码成BufferedImage或Mat对象。常见做法是用FFmpegFrameGrabber逐帧读取控制帧率避免堆积。预处理把原始帧 resize 到模型输入尺寸YOLOv5 通常是 640×640做 letterbox padding 保持宽高比像素值归一化到 0~1再转成 Numpy 数组能识别的二进制格式。数据传递Java 把预处理后的数组写入临时文件.npy或裸float32二进制或者通过标准输入管道传给 Python 进程。文件方式更稳管道方式更快但容易死锁。模型调用Python 脚本启动后加载 ONNX 模型读取 Java 传来的数据构造输入 tensor执行session.run()拿到输出。后处理Python 侧做置信度过滤、NMS、坐标映射回原图尺寸把检测框和类别整理成 JSON 返回。结果展示Java 解析 JSON在原帧上绘制矩形框和标签输出到屏幕或推回 RTSP 流。这个链路里最容易被低估的是第 3 步的数据传递。如果每帧都启动一次 Python 进程开销会大到无法接受。实际做法是让 Python 脚本常驻通过循环读取标准输入或监听本地 socketJava 侧维护一个长连接进程。2.3 关键参数与配置项在动手改代码前先搞清楚几个必须对齐的参数参数含义典型值注意点model_input_size模型输入分辨率640×640必须与导出 ONNX 时一致conf_threshold置信度阈值0.25~0.5太低误检多太高漏检iou_thresholdNMS 的 IoU 阈值0.45重叠目标多时适当调高max_detections单帧最大检测数300影响后处理耗时frame_skip跳帧间隔1~3实时性不够时调大input_format输入数据格式float32与 Python 侧np.frombuffer对应这些参数在 Java 侧和 Python 侧要各存一份启动时通过配置文件或命令行参数对齐。我一般会在 Java 的config.properties里定义Python 脚本启动时作为参数传入避免两边硬编码不一致导致「框画歪了」这种玄学问题。3. 动手复现从解压到跑通第一帧检测3.1 环境准备与依赖安装Java 侧需要 JDK 8 以上、Maven 构建核心依赖是javacv-platform和opencvjavacv 自带。Python 侧需要 Python 3.8、onnxruntime、numpy、opencv-python。先确认版本# Java 侧 java -version mvn -version # Python 侧 python --version pip install onnxruntime numpy opencv-python如果用的是 GPU 推理把onnxruntime换成onnxruntime-gpu并确认 CUDA 版本匹配。CPU 推理在 640×640 输入下YOLOv8n 大概能到 30~50ms 一帧够做 15fps 左右的视频分析。3.2 Java 侧拉流与预处理代码Java 侧的核心任务是把视频帧变成 Python 能吃的数组。下面是一个基于FFmpegFrameGrabber的简化实现// VideoFrameGrabber.java import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.Java2DFrameConverter; import java.awt.image.BufferedImage; import java.io.*; import java.nio.FloatBuffer; public class VideoFrameGrabber { private static final int INPUT_SIZE 640; public static void main(String[] args) throws Exception { FFmpegFrameGrabber grabber new FFmpegFrameGrabber(rtsp://your-stream-url); grabber.setOption(rtsp_transport, tcp); // 避免 UDP 丢包 grabber.start(); Java2DFrameConverter converter new Java2DFrameConverter(); Process pythonProcess startPythonWorker(); try (OutputStream stdin pythonProcess.getOutputStream(); BufferedReader stdout new BufferedReader( new InputStreamReader(pythonProcess.getInputStream()))) { Frame frame; while ((frame grabber.grabImage()) ! null) { BufferedImage image converter.convert(frame); float[] inputData preprocess(image); // 写入帧长度 帧数据 writeFloatArray(stdin, inputData); stdin.flush(); String result stdout.readLine(); System.out.println(Detection: result); } } grabber.stop(); pythonProcess.destroy(); } private static float[] preprocess(BufferedImage img) { // letterbox resize 归一化输出 CHW 格式 float 数组 // 具体实现略核心是保持宽高比并填充灰边 return new float[3 * INPUT_SIZE * INPUT_SIZE]; } private static void writeFloatArray(OutputStream os, float[] data) throws IOException { ByteBuffer buffer ByteBuffer.allocate(data.length * 4); buffer.asFloatBuffer().put(data); os.write(buffer.array()); } private static Process startPythonWorker() throws IOException { ProcessBuilder pb new ProcessBuilder(python, detector.py, --model, yolov8n.onnx, --conf, 0.4); pb.redirectErrorStream(true); return pb.start(); } }这段代码的关键点rtsp_transport设为tcp能显著减少花屏和丢帧预处理后的数组按float32小端序写入Python 侧用np.frombuffer直接读Python 进程只启动一次循环处理多帧避免反复加载模型。3.3 Python 侧 ONNX 推理脚本Python 脚本负责加载模型、接收数据、推理、返回 JSON。下面是一个支持 YOLOv8 输出的版本# detector.py import sys import json import numpy as np import onnxruntime as ort import argparse def load_model(model_path): session ort.InferenceSession(model_path, providers[CPUExecutionProvider]) input_name session.get_inputs()[0].name return session, input_name def postprocess(outputs, conf_thres0.4, iou_thres0.45): # YOLOv8 输出格式: [1, 84, 8400] predictions np.squeeze(outputs[0]).T # [8400, 84] boxes predictions[:, :4] scores predictions[:, 4:] class_ids np.argmax(scores, axis1) confidences np.max(scores, axis1) mask confidences conf_thres boxes, class_ids, confidences boxes[mask], class_ids[mask], confidences[mask] # NMS 实现略返回 [x1, y1, x2, y2, conf, cls] detections nms(boxes, confidences, class_ids, iou_thres) return detections def main(): parser argparse.ArgumentParser() parser.add_argument(--model, requiredTrue) parser.add_argument(--conf, typefloat, default0.4) args parser.parse_args() session, input_name load_model(args.model) input_shape (1, 3, 640, 640) while True: raw sys.stdin.buffer.read(3 * 640 * 640 * 4) if len(raw) 3 * 640 * 640 * 4: break data np.frombuffer(raw, dtypenp.float32).reshape(input_shape) outputs session.run(None, {input_name: data}) detections postprocess(outputs, args.conf) print(json.dumps(detections.tolist()), flushTrue) if __name__ __main__: main()这里有几个容易翻车的细节sys.stdin.buffer.read必须读满固定字节数否则会截断flushTrue保证 Java 侧能及时读到结果YOLOv5 的输出格式是[1, 25200, 85]后处理要相应调整不能直接套用。3.4 跑通验证与结果查看把 Java 和 Python 两侧代码放好后先单独测 Python 脚本能不能加载模型python detector.py --model yolov8n.onnx --conf 0.4 test_input.bin如果没有报错再启动 Java 主程序。第一次跑建议用本地视频文件代替 RTSP 流排除网络因素。观察控制台输出的检测结果如果框的位置明显偏移检查 letterbox 的 padding 计算和坐标还原逻辑如果置信度全是 0检查输入数据的归一化是否和训练时一致。4. 避坑排查跨语言推理最容易翻车的五个点4.1 检测框整体偏移或缩放错误现象画出来的框位置对不上目标或者框的大小明显不对。原因letterbox 预处理时 padding 的偏移量没有正确传递到后处理阶段。YOLO 的坐标还原需要知道原始图像在 640×640 画布中的实际位置和缩放比例如果 Java 侧算了 padding 但 Python 侧不知道坐标就会错。解决把每帧的scale和pad_x、pad_y一起传给 Python后处理时先减 padding 再除以 scale。或者更简单——Java 侧只做 resize 不做 paddingPython 侧统一做 letterbox保证两边逻辑一致。4.2 Python 进程卡死或结果读取超时现象Java 侧readLine()一直阻塞视频画面不动。原因Python 脚本报错退出但异常信息被redirectErrorStream混进了标准输出Java 读到的不是 JSON 而是错误堆栈或者 Python 侧print没有flush数据留在缓冲区。解决Python 侧所有输出强制flushTrueJava 侧读取时加超时机制超时后销毁进程重启调试阶段把 Python 的 stderr 单独输出到日志文件不要和 stdout 混在一起。4.3 ONNX 模型输出维度与后处理不匹配现象Python 侧reshape报错或者 NMS 后结果为空。原因YOLOv5、YOLOv7、YOLOv8 导出的 ONNX 输出格式不同。YOLOv5 是[1, 25200, 85]YOLOv8 是[1, 84, 8400]前者需要转置后者不需要。如果拿 YOLOv5 的后处理代码跑 YOLOv8 模型维度对不上直接崩。解决在 Python 脚本里根据模型输出 shape 自动判断版本或者启动时通过参数指定--version yolov8。更稳妥的做法是导出 ONNX 时固定输出格式比如用ultralytics导出时加simplifyTrue。4.4 RTSP 流断线后程序不恢复现象网络波动后视频流断了程序一直卡在原地不重连。原因FFmpegFrameGrabber在流断开后grabImage()返回 null但循环没有处理这种情况直接退出了。解决在循环里判断frame null时执行grabber.stop()和grabber.start()重连加重试次数上限和退避间隔。常见做法是重试 3 次每次间隔 2 秒超过后记录日志并告警。4.5 CPU 推理速度跟不上视频帧率现象检测结果滞后于视频画面越跑越慢。原因每帧都做完整推理但 CPU 单帧耗时超过帧间隔导致帧堆积。解决加跳帧逻辑每 2~3 帧处理一次或者用onnxruntime-gpu切到 GPU 推理还可以把输入尺寸从 640 降到 416 或 320速度能提升一倍以上代价是小目标检测精度下降。我一般会先测一下当前硬件的单帧耗时再决定跳帧间隔。5. 进阶技巧让这套方案在生产环境跑得更稳5.1 用共享内存替代标准输入输出标准输入输出在数据量大时会有拷贝开销640×640×3 的 float32 数组每帧约 4.9MB30fps 就是 147MB/s 的管道吞吐容易成为瓶颈。进阶做法是用共享内存Java 侧用FileChannel.map创建内存映射文件Python 侧用np.memmap直接读同一块内存零拷贝。代价是要处理同步问题——Java 写完一帧后通过信号量通知 PythonPython 读完再通知 Java 写下一帧。这套机制在yolo-onnx-java包里没有直接提供但改起来不难适合对延迟敏感的场景。5.2 模型量化与输入尺寸的权衡ONNX 模型支持 INT8 量化能把推理速度提升 2~3 倍模型体积缩小到原来的四分之一。但量化后的精度损失在目标检测任务上比较明显尤其是小目标和密集场景。我的经验是如果检测目标尺寸较大、类别区分度高INT8 量化完全可用如果是安全帽检测、车辆检测这类需要区分相似类别的场景还是用 FP32 或 FP16。输入尺寸方面640 是精度和速度的平衡点416 适合对实时性要求高但目标较大的场景320 基本只能做粗略检测。5.3 多路视频流的进程管理生产环境往往要同时处理多路 RTSP 流。如果每路流都启动一个 Python 进程内存和 CPU 会迅速吃满。更好的做法是让一个 Python 进程服务多路流用多线程或异步 IO 处理推理请求。但 ONNX Runtime 的session.run()本身是线程安全的可以在 Python 侧维护一个线程池Java 侧通过 socket 把不同流的帧发到同一个 Python 服务带上流 ID 做区分。这样模型只加载一次显存和内存占用都可控。5.4 验证检测结果是否正确的土办法调参调到最后怎么确认检测结果是对的我一般会做两件事第一用一张已知答案的测试图比如bus.jpg跑一遍把画框后的图保存下来肉眼比对确认框的位置和类别都对第二统计连续 100 帧的检测数量如果波动特别大比如从 5 个跳到 50 个说明置信度阈值或 NMS 参数有问题。还有一个笨办法但很有效把 Python 侧返回的原始输出和 Java 侧画框后的结果同时打印出来逐帧对比坐标偏移超过 2 个像素就说明预处理或后处理有 bug。从那以后我每次接入新的 ONNX 模型都会先用一张固定测试图跑通全链路确认坐标还原无误后再接视频流。这个习惯帮我省掉了至少三次「框画歪了但找不到原因」的返工。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Windows C盘空间清理工具实践:图形化磁盘占用分析与安全删除提示

Windows C盘空间清理工具实践:图形化磁盘占用分析与安全删除提示

地址 https://freedw.com/archives/8058 系统盘为什么会不知不觉被占满 系统更新残留、临时文件、运行日志、休眠与页面文件、各类软件的缓存,都会持续堆积在系统盘里。这些内容单个体积不大、数量却很多,靠手动翻目录很难判断空间到底被谁吃掉了。 动…

2026/10/11 22:34:24 阅读更多 →
OpenCode + Strata 混合推理:本地加速、云端兜底的工程工作流搭建

OpenCode + Strata 混合推理:本地加速、云端兜底的工程工作流搭建

OpenCode Strata 混合推理:本地加速、云端兜底的工程工作流搭建 【免费下载链接】Strata Qwen3.8-Flash-Next on any consumer hardware: one-click install for Windows / Linux. Strata inference engine, OpenAI/Anthropic API on localhost, optional image in…

2026/10/11 22:34:24 阅读更多 →
输电线异物检测数据集VOC+YOLO格式1300张4类别:YOLOv8训练与提点实战

输电线异物检测数据集VOC+YOLO格式1300张4类别:YOLOv8训练与提点实战

简介:本资源为输电线异物检测数据集,面向电力巡检、无人机视觉检测方向的算法工程师与高校研究者,用于训练和验证balloon、kite、nest、trash四类异物目标检测模型。压缩包共2000个文件,包含1300张jpg图片及一一对应的1300个VOC格…

2026/10/11 22:33:23 阅读更多 →

最新新闻

PS5硬件扩展边界全解析:接口、协议与物理限制

PS5硬件扩展边界全解析:接口、协议与物理限制

项目标题“AnyPS5”目前在公开网络中无权威技术文档、官方产品发布或主流科技媒体报导支撑,亦未见于索尼互动娱乐(Sony Interactive Entertainment)任何已知产品线、开发代号或公开技术白皮书。经多平台语义检索与行业术语交叉验证&#xff0…

2026/10/11 23:15:10 阅读更多 →
YOLOv8+PaddleOCR车牌识别全链路工程实践

YOLOv8+PaddleOCR车牌识别全链路工程实践

简介:本资源是一套面向本科毕业设计与人工智能课程实践的智能车牌识别系统完整实现方案,聚焦图像识别与机器学习在智能交通场景中的落地应用。系统基于YOLOv8目标检测与PaddleOCR文字识别双引擎协同架构,覆盖车牌定位、字符分割、OCR识别全流…

2026/10/11 23:15:08 阅读更多 →
无电压传感器三相PWM整流器虚拟磁链定向仿真全解析

无电压传感器三相PWM整流器虚拟磁链定向仿真全解析

1. 这个项目到底在做什么说句实在话,三相PWM整流器的Simulink仿真,我在学生时代和工程实践里都反反复复碰过很多次。电压型PWM整流器这一块,最常见的控制策略是电压定向矢量控制(VOC),也就是把三相交流量通…

2026/10/11 23:15:06 阅读更多 →
RWKV7-G1 0.1B 推理模型发布:把纯血 RNN 塞进嵌入式设备的 TaoToken 实测路径

RWKV7-G1 0.1B 推理模型发布:把纯血 RNN 塞进嵌入式设备的 TaoToken 实测路径

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 23:15:04 阅读更多 →
RAP消息被截断排查:从50字符到220字符的嵌入式通信避坑指南

RAP消息被截断排查:从50字符到220字符的嵌入式通信避坑指南

上周我接到一个很典型的排查任务:现场反馈物联网关上报到平台的消息“丢尾”,一条本该有 80 个字符的 RAP 消息,对端只收到了 50 个字符。第一反应是 TCP 拆包没处理好,查了一圈才意识到,根本不是网络问题,…

2026/10/11 23:14:59 阅读更多 →
车载空调系统建模全流程:从热力学方程到量产图纸

车载空调系统建模全流程:从热力学方程到量产图纸

车载空调这东西,看着是个普普通通的汽车零部件,真要较真起来能让人头大一圈。热力学、流体力学、控制理论、结构设计全搅在一起,你光会仿真或者光会画图都不够,得从算法推导一路干到图纸落地才算是真本事。我这些年折腾车载空调建…

2026/10/11 23:13:59 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →