新闻详情

新闻详情

首页 / 资讯中心 / 详情

Label-studio ML后端接入YOLOv8-OBB:旋转框半自动标注实现

发布时间:2026/9/30 13:31:41来源:尧图网络
Label-studio ML后端接入YOLOv8-OBB:旋转框半自动标注实现
简介这是一份面向Label Studio用户的YOLOv8-OBB模型ML后端代码专为旋转目标检测的半自动标注场景设计适合遥感图像、工业质检等需要标注带角度矩形框的项目使用者。压缩包为zip格式仅1个py文件大小2KB属于轻量级插件脚本。该代码文件对接Label Studio ML后端接口能够加载YOLOv8-OBB模型将推理得到的旋转框坐标、角度与类别转换为平台可识别的标签结构并在标注界面生成预标注框标注人员只需微调起终点与角度即可快速确认结果显著减轻手动绘制工作量。资源虽小但完整覆盖了从模型加载、推理输出到标签映射的关键链路可直接搭配自有权重嵌入现有标注流程。目前已有831人学习下载适合有一定Python基础、熟悉Label Studio或YOLO生态希望建立自动标注流程的开发者参考。通过这份代码可以掌握ML后端API的编写方式、OBB预测结果的解析与标签映射技巧结合作者提供的教程能更快在本地完成环境配置和测试。1. 半自动标注离不开这个 Model.pyLabel-studio ML 后端接入 YOLOv8-OBB 的最小实现做过遥感影像或者工业质检标注的人都知道旋转框标注比普通水平框痛苦得多。鼠标要旋转四五十次才能把一个倾斜目标框准一天下来手腕都是酸的。Label-studio 本身支持 OBB 格式的标注但默认是纯手动没有 AI 预标注能力。想让它像检测任务那样「画出大概的框人工只调角度和边界」就必须接一个 ML 后端。而这份资源里的 Model.py正是把已经训练好的 YOLOv8-OBB 模型塞进 Label-studio 的最小实现。它解决了「Label-studio 发出的预测请求怎么被 YOLOv8 接收、推理结果怎么转成标注工具认识的旋转框」这个核心衔接问题。适合正在用 Label-studio 做半自动标注、自己手里有 .pt 权重但不想折腾前后端联调的检测工程师。看完这篇文章你能直接照着改出一个能跑通的 OBB 预标注后端。2. 先搞懂 ML 后端的运行机制Label-studio 从预测请求到返回标注的完整链路2.1 Label-studio ML 后端是什么为什么需要单独写 Model.pyLabel-studio 的 ML 后端不是一个插件而是一个独立运行的 HTTP 服务。标注页面里每来一张图前端会带着当前任务信息和这张图的 URL 去请求这个服务服务内部调用你的模型做推理再把结果返回给前端渲染成标注框。这个服务可以跑在本地也可以跑在单独的机器上只要 Label-studio 能访问到它的地址就行。Model.py是这个服务的核心文件。它里面定义了一个继承自LabelStudioMLBase的类重写了predict()方法。Label-studio 的官方 SDK 已经帮你把 Flask 服务、路由分发、任务解析这些杂活处理好了你真正需要写的就只是「拿到一张图跑模型返回标注结果」。也就是说Model.py 是算法工程师参与后端逻辑的入口也是这份资源的精华所在。如果你之前只跑过单独的 YOLOv8 训练脚本不理解为什么不能直接让 Label-studio 调用yolo predict这里就要想明白一件事Label-studio 希望你的后端返回的是结构化的 JSON里面包含每个目标的类别、置信度、坐标数据而不是一张画好框的图片。Model.py 就是负责把 YOLOv8 的输出结果重新组织成 Label-studio 能识别的协议格式。2.2 请求与响应协议predict 接口的输入输出格式Label-studio 每次调用 ML 后端时发送的 body 是一个 JSON核心字段是tasks和label_config。tasks里包含任务 ID、数据内容比如一张图片的 URL 或本地路径而label_config是你在 Label-studio 里配置的标注 XML 模板。predict()方法拿到这些内容后需要自己做图像加载、模型推理然后返回一个列表列表里每个元素对应一个标注结果。响应格式长这样这是一个 OBB 目标的结果{ result: [ { id: abc123, from_name: label, to_name: image, type: rectanglelabels, value: { x: 105.6, y: 88.2, width: 120.4, height: 80.9, rotation: 15.3, rectanglelabels: [question, 0.92] }, read_only: false } ], score: 0.92 }这里要注意x和y是旋转框中心点的坐标width和height是外接水平矩形实际上对于旋转框Label-studio 更常用的是外接矩形加旋转角度。rotation是以水平为基准的旋转角度单位是度范围通常是 -180 到 180。rectanglelabels这个字段里需要放两个值第一个是你在标注模板里配置的标签名第二个是置信度。这个地方非常容易踩坑很多人第一次返回结果后发现前端渲染不出框就是因为标签名跟label_config里的名称没对上。Label-studio 官方 SDK 在拿到响应后会直接把result渲染到标注页面上。假如你返回的坐标值超出了图像像素范围前端会报错或者框跑到画布外面。所以坐标转换这一步必须严谨处理我在下一章里会详细讲 OBB 输出的转换过程。3. 把 YOLOv8-OBB 塞进 Model.py模型加载、预处理与推理实现3.1 模型初始化与权重加载不要让模型每次预测都重复初始化一个常见的错误写法是在predict()方法里写YOLO(last.pt)因为predict()会被每个任务调用一次这样每标注一张图模型权重就从磁盘加载一次慢得让人怀疑人生。正确的做法是在类的__init__或setup阶段初始化模型之后每次预测只调用model.predict()。下面是这份资源里Model.py的典型初始化代码import os import requests import numpy as np from ultralytics import YOLO from label_studio_ml.model import LabelStudioMLBase from label_studio_ml.utils import get_image_local_path class YOLOv8OBBModel(LabelStudioMLBase): def __init__(self, model_path./weights/last.pt, **kwargs): super().__init__(**kwargs) self.model YOLO(model_path) self.model.conf float(os.getenv(CONF_THRESHOLD, 0.35)) self.model.iou float(os.getenv(IOU_THRESHOLD, 0.45))初始化逻辑说明model_path指向你训练好的 YOLOv8-OBB 权重通常是.pt文件。我把置信度阈值和 IoU 阈值从环境变量里读这样不用改代码就能调参后面会专门讲这个技巧。super().__init__(**kwargs)这行必须保留否则 Label-studio SDK 里的self.label_config、self.parsed_label_config等属性不会被初始化后面解析标签名时会报空属性错误。3.2 图像预处理与 OBB 输出的后处理从旋转框到 Label-studio 格式Label-studio 传进来的图像地址可能是一个 URL也可能是一个本地路径。官方 SDK 提供了get_image_local_path函数会自动判断并把远程图片下载到本地临时目录。拿到图片路径后直接把它传给 YOLOv8 的predict()方法。核心的predict方法实现如下def predict(self, tasks, **kwargs): results [] for task in tasks: # 从任务数据中取图片路径或URL image_url task[data][image] local_path get_image_local_path(image_url, tasktask) raw_image cv2.imread(local_path) orig_h, orig_w raw_image.shape[:2] # 跑推理 detections self.model.predict(local_path, verboseFalse)[0] # 遍历YOLOv8-OBB的detections if detections.obb is None: results.append({result: [], score: 0.0}) continue prediction [] for i in range(len(detections.obb.boxes)): # obb的xywhr是归一化到0-1? 不是像素坐标但置信度需要过滤 confidence float(detections.obb.conf[i]) if confidence self.model.conf: continue x_center, y_center, width, height, angle detections.obb.xywhr[i].tolist() cls_id int(detections.obb.cls[i]) cls_name self.model.names[cls_id] # 坐标像素值 - Label-studio比例坐标可选 # Label-studio默认像素坐标如果前端显示异常可以转比例 prediction.append({ id: fobb_{i}, from_name: label, to_name: image, type: rectanglelabels, value: { x: float(x_center), y: float(y_center), width: float(width), height: float(height), rotation: float(angle) * 180.0 / np.pi, rectanglelabels: [cls_name, f{confidence:.2f}] }, read_only: False }) results.append({result: prediction, score: prediction[0][value][rectanglelabels][1] if prediction else 0.0}) return results这里有个关键细节detections.obb.xywhr返回的五个值中xywhr表示中心 x中心 y宽高旋转角弧度。而 Label-studio 的rotation字段需要的是角度度所以必须用angle * 180.0 / np.pi做转换。如果你忘了做这个转换旋转框会以弧度的视觉比例呈现在画布上基本上是一个完全不对的形状。另一个重要问题是坐标单位。YOLOv8-OBB 的输出坐标是相对于原始图像尺寸的像素坐标而有些版本的 Label-studio 前端期望的是归一化比例0-1。如果你在标注页面上发现框的位置严重偏移尤其是图片被缩放显示时可以在上面代码中把x除以orig_w、y除以orig_h但要注意width和height也要同时转成比例值。我这里默认返回像素坐标因为绝大多数 Label-studio 配置里image字段的zoom模式会自己换算具体可以看你的标注模板里的Image标签配置。4. 跑通半自动标注本地启动 ML 后端并接入 Label-studio4.1 环境依赖与目录结构这份资源里有哪些文件这份资源解压后大约是一个标准的 Label-studio ML 后端项目. ├── Model.py ├── _wsgi.py ├── Dockerfile ├── requirements.txt └── weights/ └── last.ptModel.py是核心文件里面已经写好了 YOLOv8-OBB 的加载、推理和结果转换逻辑。_wsgi.py是启动入口它会创建 Flask 应用并把 Model.py 里的类实例注册到/models路由。requirements.txt里声明了label-studio-ml、ultralytics、opencv-python-headless等依赖。如果你没有 Docker 环境可以直接用 pip 安装依赖后跑_wsgi.py启动。我一般会先用一个虚拟环境跑起来避免把系统 Python 环境搞乱。创建虚拟环境并安装依赖的命令如下python3 -m venv labelstudio-ml-env source labelstudio-ml-env/bin/activate pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后确认ultralytics能正常导入并且weights/last.pt文件存在。如果权重文件缺失服务也能启动但第一个预测请求会直接报错所以最好在启动前先做一个本地推理测试python -c from ultralytics import YOLO; m YOLO(weights/last.pt); print(model loaded)4.2 启动服务并配置 Label-studio 的机器学习后端启动 ML 后端服务需要指定监听端口默认是 9090。直接用 python 运行_wsgi.pypython _wsgi.py --port 9090如果你在远程服务器上跑想让别的机器也能访问需要加--host 0.0.0.0python _wsgi.py --host 0.0.0.0 --port 9090启动成功后你会看到类似Running on http://0.0.0.0:9090的日志。此时可以测试这个服务的健康状态curl http://localhost:9090/health返回{status: UP}就说明服务正常。接下来打开 Label-studio 的 Web 界面进入项目设置Settings找到 Machine Learning 页面点击 Add Model。在 URL 栏填上http://localhost:9090Label-studio 会自动调用这个地址下的/predict接口做验证。验证通过后再去标注页面时右侧智能标注Smart labeling区域就会显示该模型点一下就能对当前图片执行预标注了。这里有一个必须提前确认的点你的 Label-studio 项目里必须配置了对应的rectanglelabels标签并且from_name和to_name要和 Model.py 里写的一致。如果标注模板里标签名是obj而 Model.py 返回的是question预标注结果会被前端丢弃但不会报错。排查这种问题很容易让人抓狂我通常会在 Model.py 里把返回结果打印一份 JSON 到日志然后对比实际渲染的标注框。5. 避坑指南OBB 坐标转换、环境版本和并发预测的典型问题5.1 现象旋转框坐标总是偏移框和目标的中心对不上原因YOLOv8-OBB 输出的是原始图像像素坐标但 Label-studio 的Image标签在标注界面里经过了缩放适配前端在渲染时如果没有按比例换算就会出现中心点偏移。另一个原因是部分版本 Label-studio 期望x、y是归一化坐标相对图片宽高的比例。解决在 Model.py 的predict方法里获取原始图像的宽高对输出坐标做一步除法即可。我习惯这样写value { x: x_center / orig_w, y: y_center / orig_h, width: width / orig_w, height: height / orig_h, rotation: angle * 180.0 / np.pi, }但要注意你需要在标注配置里确认 Label-studio 的坐标模式。可以在项目设置的 Labeling Interface 里看到Image标签如果它设置了zoomtrue前端会按展示比例自动缩放此时返回像素坐标反而正确。最稳妥的方法是直接用返回像素坐标试一次如果不准再改成归一化。5.2 现象第一个预测请求极慢之后每次预测都很慢甚至内存溢出原因模型在predict()方法里被反复加载。如果你继承的类中setup方法没有写而predict里有YOLO(...)句那么每处理一张图就会把权重和 CUDA 上下文重新创建一次显存会快速被打满。解决把模型加载放到__init__或setup中。如果发现多线程并发时有报错可以用torch.multiprocessing或给 Flask 服务加--threaded参数控制并发。我的经验是模型的predict方法内部已经是线程可重入的但最好还是把self.model作为只读对象不要在predict里修改模型的conf属性。5.3 现象Label-studio 连接 ML 后端失败提示连接拒绝或超时原因启动 ML 后端的机器和 Label-studio 所在机器不在同一网络或者监听地址是 127.0.0.1导致外部无法访问。如果你在服务器上只写了python _wsgi.py --port 9090它默认监听 127.0.0.1外部机器自然连不上。解决启动时加--host 0.0.0.0。还要检查防火墙如果是云服务器需要把对应端口的入站规则打开。另外Label-studio 设置页面里填写的 URL 不能带/predict路径只需要填根地址SDK 会自动拼接。5.4 现象半自动标注返回空结果前端不出现任何框原因推理结果里所有目标的置信度都被过滤掉了或者标签名映射失败。置信度阈值设置过高时比如conf0.5但你的模型普遍输出 0.3 的置信度就会空手而归。另一个原因是rectanglelabels里填写的标签名在 Label-studio 中不存在或者标签名带了空格。解决先把CONF_THRESHOLD环境变量调到 0.1 测试确认能出框后再逐步调高。打印cls_name和from_name的日志确保与项目模板完全一致。标签名里不要有多余字符用strip()去掉空格更保险。6. 让 Model.py 更进一步把 OBB 预测结果可视化回传并调优阈值6.1 在 Model.py 中实现预测结果的可视化验证调试 OBB 后端时最头疼的问题是你不知道前端到底把框渲染成了什么样。一个比较实用的技巧是在predict()里顺手用 OpenCV 把 YOLOv8-OBB 的结果画在图片上保存到本地或者回传给前端。这样你既能检查坐标转换是否正确也能直观看到模型的预测质量。我通常会在推理后加入这段代码专门用来定位坐标问题import cv2 # 在原图上画出旋转框 draw_img cv2.imread(local_path) for i in range(len(detections.obb.boxes)): # 用 cv2.boxPoints 画旋转矩形需要知道中心、宽高和角度 box cv2.boxPoints((center_x, center_y, width, height, angle_deg)) box np.int0(box) cv2.polylines(draw_img, [box], True, (0, 255, 0), 2) cv2.putText(draw_img, f{cls_name} {conf:.2f}, (int(x_center), int(y_center)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) cv2.imwrite(/tmp/debug_obb.jpg, draw_img)注意cv2.boxPoints的最后一个参数需要角度而 YOLOv8-OBB 的xywhr里是弧度必须先用angle_deg angle * 180.0 / np.pi转换。保存出来的图片可以直接打开看如果框偏了八成是坐标单位或角度问题。这个调试习惯非常管用我一度被旋转框的坐标搞到怀疑人生后来就用这种方式几分钟就能定位问题出在哪个环节。6.2 通过环境变量动态调整模型参数避免反复改代码不同的标注任务对预标注的容忍度不同。比如你是标注交通标志希望置信度低一些也能出框人工只要确认就行而如果目标是密集的建筑物置信度太高会很烦太低又会框得乱七八糟。与其每次改代码重启服务我建议在__init__里从环境变量读取这些参数import os self.conf_threshold float(os.getenv(CONF_THRESHOLD, 0.25)) self.iou_threshold float(os.getenv(IOU_THRESHOLD, 0.45))然后在predict()里调用self.model.predict(local_path, confself.conf_threshold, iouself.iou_threshold)。这样启动服务时只需要设置环境变量就能动态调整不用改 Model.py 的代码CONF_THRESHOLD0.15 IOU_THRESHOLD0.5 python _wsgi.py --port 9090这个习惯让我在后端调试时省了很多事。后来我甚至把标签名映射也做成环境变量用逗号分隔的形式指定CLASS_MAP这样换一个项目权重时连 Model.py 都不用改直接设置新的映射就能跑。从那以后我每次拿到一个新的检测项目第一件事就是确认 Model.py 里的坐标单位、标签名和环境变量开关再连上 Label-studio 做一次真实图片的预标注验证。这套流程走下来基本不会再被旋转框预标注的对接问题卡住。希望这篇拆解能帮你少走几个弯路希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

jevgrep 评测全揭秘:SWE-bench 10 任务 8/10 通过、总成本降 25.8% 的完整方法论 2026/9/30 14:57:53

jevgrep 评测全揭秘:SWE-bench 10 任务 8/10 通过、总成本降 25.8% 的完整方法论

jevgrep 评测全揭秘:SWE-bench 10 任务 8/10 通过、总成本降 25.8% 的完整方法论 【免费下载链接】jevgrep Find code by asking what it does. A CLI for coding agents that uses Jev to discover relevant files and source context. 项目地址: https://gitcod…

阅读更多 →
GEO专家孟庆涛:GEO 时代的信源布局方法论从内容优化到语境匹配 2026/9/30 14:57:30

GEO专家孟庆涛:GEO 时代的信源布局方法论从内容优化到语境匹配

当 AI 的推荐随问法、语言、城市与平台漂移,品牌要优化的就不再是内容本身,而是内容与语境的匹配概率。 2026 年 9 月 7 日,Semrush 与 Exploding Topics 联合发布了一项覆盖 2338 名美国成年人的调查:73.6% 的每周 AI 使用者曾依…

阅读更多 →
Flink流处理架构演进:从状态管理到CDC Pipeline与批流一体实践 2026/9/30 14:57:22

Flink流处理架构演进:从状态管理到CDC Pipeline与批流一体实践

做流计算这几年,有个特别明显的感受:只要是聊大数据实时计算,Flink几乎是绕不开的名字。从面试题里的“Flink和Spark Streaming有什么区别”,到毕业设计里的“电商实时大屏”,再到生产环境里的“CDC Pipeline整库同步”…

阅读更多 →
从CPU到内存:一文读懂冯诺依曼体系结构与性能瓶颈 2026/9/30 14:57:22

从CPU到内存:一文读懂冯诺依曼体系结构与性能瓶颈

做了这么多年开发,带过的实习生和刚入行的同事少说也有几十个,我发现一个规律:很多人写了好几年代码,能把各种框架调得飞起,但你要是问他CPU到底是怎么把一行a b c变成结果的,十有八九会卡壳。聊到冯诺依…

阅读更多 →
VMware中Ubuntu 22.04虚拟机磁盘扩容完整指南:从分区到LVM一步到位 2026/9/30 14:57:21

VMware中Ubuntu 22.04虚拟机磁盘扩容完整指南:从分区到LVM一步到位

不知道你有没有遇到过这种情况:VMware里装了个Ubuntu 22.04,当时觉得自己挺有经验,硬盘随便给了20G,结果过了一两个月,编译一个大项目、拉几个Docker镜像、再装点ROS依赖,系统盘就飘红了。清理缓存、删日志…

阅读更多 →
基于SpringBoot+Vue3的果蔬生鲜电商系统:前后端分离与JWT鉴权实战解析 2026/9/30 14:57:21

基于SpringBoot+Vue3的果蔬生鲜电商系统:前后端分离与JWT鉴权实战解析

先把我做这个项目的真实感受放在最前面:没有任何一个技术项目能像果蔬生鲜电商这样,把SpringBoot和Vue3的实战价值体现得如此充分。前后端分离、JWT鉴权、商品与订单流转、后台管理……这些看上去很“教科书”的名词,落在一个卖菜平台上&…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉