ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Label Studio集成YOLOV8-OBB:旋转目标预标注实战指南

Label Studio集成YOLOV8-OBB:旋转目标预标注实战指南 简介面向Label Studio半自动标注需求资源提供YOLOv8目标检测OBB旋转框模型的Model.py后端文件适合需要为Label Studio接入旋转目标检测能力的标注团队或个人开发者。Model.py是ML后端核心脚本负责将YOLOv8-OBB的预测结果转换为Label Studio可识别的标注格式支持自动生成旋转边界框再交由人工确认修正形成“自动预测—人工审核”的高效半自动标注闭环。整包为zip压缩格式仅含1个Python脚本大小仅2KB结构单一部署和迁移非常轻量用户可根据自身模型路径和类别配置微调便于快速接入现有流程。目前已有829人浏览/学习说明该方案在相关社区有一定关注度可作为旋转框标注场景下的实用参考。获取后可快速复用该后端逻辑将YOLOv8-OBB能力接入Label Studio直接服务于遥感影像、工业质检等旋转目标标注项目显著减少重复框选工作量。 Label Studio 的 ML 后端配合 YOLOV8-OBB 做预标注是我最近在遥感数据集项目里反复迭代后确定下来的一套方案。做旋转目标检测的数据标注有多痛苦做过的人应该深有体会普通矩形框框不住带角度的目标手工画 4 点多边形又慢又容易歪。把 YOLOV8-OBB 模型接进 Label Studio让它先画一轮带角度的预测框人工只需要做修正效率提升非常明显。这篇文章我就以 Model.py 文件为主线把整个 ML 后端的实现思路、坐标转换逻辑、部署验证过程以及我踩过的坑一次性讲清楚。适合正在做 OBB 数据标注、想给团队接预标注能力的工程师参考。1. 为什么要在 Label Studio 里接 YOLOV8-OBB1.1 旋转框标注的痛点普通目标检测标注一个框只需要拉两个对角点OBB 就不一样了。飞机、舰船、工业零件、无人机视角下的车辆这些目标往往有明显的朝向用 axis-aligned 的矩形框去框要么框住太多背景要么把相邻目标框在一起。Label Studio 虽然自带各种标注工具但纯手工画 4 点框非常累——点完 4 个点还要手动调整每条边的贴合度一张图几十上百个目标的时候标注员心态很容易崩。这个问题在遥感场景里尤其突出。一张大图上几十个目标分布在不同角度用普通标签工具一个个描边不仅速度慢而且不同标注员画出来的框一致性很差。模型训练时对标注框的一致性其实很敏感标注质量不稳定后面训练出来的模型边界精度也会跟着打折扣。1.2 ML 后端的价值Label Studio 的 ML 后端其实是一个独立的 HTTP 服务通过实现LabelStudioMLBase类和predict()方法把模型推理能力暴露给标注平台。标注员在界面上打开一张新图平台会自动请求这个服务把模型预测结果以预标注的形式渲染到画布上。对于 OBB 这种结构复杂的标注预标注的意义比普通矩形检测更大。人工从零画一个旋转框可能需要十几秒修正一个已经基本贴合目标的旋转框可能只需要一两秒。整体下来标注效率至少翻倍而且因为预标注框的贴合度比较统一最终产出的标注数据质量也会更稳定。1.3 为什么选 YOLOV8-OBB 而不是其他旋转框方案Ultralytics 的 YOLOV8 从 8.x 开始原生支持 OBB 任务收敛速度快、推理速度快、导出部署也方便。相比传统旋转框检测方案训练一条命令就能跑自带的标注格式也很简洁拿到模型权重后不用写复杂的前处理代码。另一个关键点是生态成熟。社区里关于 YOLOV8-OBB 的训练教程、数据格式说明、部署样例都很多遇到问题搜索一下基本能找到答案。对工程落地来说选一个周边资料丰富的方案后续维护成本会低很多。Label Studio 这边本身也是开源项目ML 后端接入逻辑不复杂两边都很容易上手。2. 动手前必懂的坐标体系OBB 与 Label Studio 的格式差异2.1 YOLOV8-OBB 输出的 xywhr 是什么YOLOV8-OBB 推理后result.obb对象里主要包含三个东西xywhr、conf、cls。xywhr的每一行是[cx, cy, w, h, angle]其中cx、cy是旋转框中心点的像素坐标w、h是宽和高angle是弧度制的旋转角逆时针为正。注意这里是弧度不是角度很多人第一次接触在这里栽跟头。可以想象一把尺子平放在桌面上默认水平时角度为 0逆时针转 90 度则angle约等于 1.5708pi/2。YOLO 官方在数据标注阶段约定、训练和推理都沿用这一套所以写转换代码时不需要额外换算单位直接用math.cos/math.sin就行。2.2 Label Studio 的 polygon 标注格式Label Studio 里的旋转框最稳妥的表示方式是 Polygon也就是用 4 个角点描述一个四边形。一个 result 里的value字段包含points和polygonlabelspoints是一个 4 点坐标数组每个点是[x, y]坐标系是归一化百分制x、y的范围是 0 到 100分别相对于图片宽度和高度的百分比。这里我特意强调一下因为很多人第一次对接时直接把像素坐标填进去前端会画出一个超出画布几百倍的框。还有original_width和original_height必须填真实像素宽高前端才能正确换算。如果这两个值传错了即使 points 的百分制坐标算对了框的位置也会偏移。2.3 从 xywhr 到四角点的换算过程其实就是以中心点为中心把半宽半高按旋转角做一次二维旋转。可以分三步理解生成未旋转的四个相对角点相对中心(-w/2, -h/2)、(w/2, -h/2)、(w/2, h/2)、(-w/2, h/2)用旋转矩阵把相对角点旋转angle弧度加上中心点坐标得到四个绝对像素坐标。代码实现后面会给出。这里先提一个容易踩的细节旋转矩阵的写法有很多种坐标轴的 y 正方向不同会导致框的旋转方向看起来相反。用cv2.imread拿到的图像坐标系是 y 向下为正所以旋转框转出来以后最好先用 OpenCV 画在原图上验证一下再接到 Label Studio 里不要凭感觉直接上线。3. Model.py 完整实现与逐段讲解3.1 依赖与初始化先列一下运行环境Python 3.9label-studio-mlML 后端框架、ultralyticsYOLO 权重推理、opencv-python和Pillow图像读取。启动服务前确认 torch 版本和 CUDA 正常如果机器没有独立显卡也不至于完全不可用CPU 推理单张图大约 1 到 3 秒预标注场景数据量不大时勉强能接受有条件的话还是建议用 GPUOBB 推理在小模型上也只有几十毫秒。初始化部分有两个关键点一个是super().__init__(**kwargs)这行代码让 ML 后端框架把 Label Studio 传来的标注配置、项目信息注入进来另一个是读取模型的类别映射self.model.names它是一个从 class id 到类别名的字典。后面生成预测结果时必须用这个字典把数字 id 转成字符串标签名否则 Label Studio 前端匹配不上。3.2 predict 方法从影像到预标注结果完整代码如下我加了详细注释import math import numpy as np from PIL import Image from label_studio_ml.model import LabelStudioMLBase from label_studio_ml.response import ModelResponse from label_studio_ml.utils import get_local_path from ultralytics import YOLO class YOLOV8OBBModel(LabelStudioMLBase): def __init__(self, model_path, **kwargs): super().__init__(**kwargs) self.model_path model_path self.model YOLO(model_path) # model.names 是 {0: class_name, 1: class_name, ...} self.labels list(self.model.names.values()) def _xywhr_to_corners(self, cx, cy, w, h, angle): 将 YOLOV8-OBB 的 xywhr 转为 4 个角点像素坐标 cos_a, sin_a math.cos(angle), math.sin(angle) half_w, half_h w / 2, h / 2 # 未旋转时的四个相对角点 corners np.array([ [-half_w, -half_h], [half_w, -half_h], [half_w, half_h], [-half_w, half_h], ]) # 二维旋转矩阵 rot np.array([ [cos_a, -sin_a], [sin_a, cos_a], ]) corners corners rot.T corners[:, 0] cx corners[:, 1] cy return corners.tolist() def predict(self, tasks, contextNone, **kwargs): # Label Studio 会一条任务一条任务调用这个接口 task tasks[0] image_url task[data][image] # 兼容 http URL 和本地路径内部会做缓存 image_path get_local_path(image_url, task_idtask.get(id)) # 读取真实图片宽高用于像素坐标转百分制坐标 with Image.open(image_path) as img: img_w, img_h img.size # 推理参数按实际场景调整 results self.model.predict( sourceimage_path, imgsz1024, conf0.25, iou0.45, device0, verboseFalse, ) predictions [] result results[0] # 模型没检测到任何目标时返回空预测而不是报错 if result.obb is None: return ModelResponse(predictionspredictions) obb result.obb xywhr obb.xywhr.cpu().numpy() # shape: [N, 5] confs obb.conf.cpu().numpy() cls_ids obb.cls.cpu().numpy().astype(int) for i in range(len(cls_ids)): cls_id int(cls_ids[i]) conf float(confs[i]) cx, cy, w, h, angle xywhr[i] corners self._xywhr_to_corners(cx, cy, w, h, angle) # 像素坐标转 0-100 的百分制坐标 points [] for px, py in corners: px round(px / img_w * 100, 2) py round(py / img_h * 100, 2) points.append([px, py]) cls_name self.labels[cls_id] predictions.append({ result: [{ from_name: label, to_name: image, type: polygon, original_width: img_w, original_height: img_h, image_rotation: 0, value: { points: points, polygonlabels: [cls_name], }, score: conf, id: fobb_{i}, }], }) return ModelResponse(predictionspredictions)整个 predict 方法的流程是拿到任务、读取图片、推理、把每个旋转框转成 Label Studio 的 result 列表。get_local_path()会把 URL 转成本地缓存路径如果图片已经在本机就直接返回原路径。推理时设置的imgsz1024最好和模型训练时一致conf0.25是预标注场景一个不错的起点后续可以按数据情况微调。3.3 角点转换工具函数_xywhr_to_corners这个方法值得单独讲。我见过不少人直接把xywhr里的w和h当成普通矩形的宽高忽略了旋转矩阵这一步结果画出来的框永远是水平的角度完全丢失。旋转矩阵部分corners rot.T等价于把每个相对角点向量做旋转。我习惯写成矩阵乘法而不是循环因为numpy一次算完代码更简洁推理循环里调用也不会成为性能瓶颈。转出来的四个角点顺序是左上、右上、右下、左下按这个顺序连起来就是一个正常的凸四边形。如果前端发现框交叉比如出现蝴蝶结形状的四边形那就说明角点顺序有问题可以在转换后对四个点按角度排序强制保证顺时针或逆时针。实际项目中YOLOV8-OBB 转出来的框基本都是正常凸四边形这个情况我遇到得不多。3.4 如果还想用 fit 触发训练Label Studio 的模型界面会有一个 Train 按钮点击后会调用 ML 后端的fit()方法。这个方法接不接都行取决于你的工作流。如果你希望标注到一定量后在平台里一键触发训练就在fit()里写训练命令如果你习惯命令行跑训练fit()可以只返回一个提示。def fit(self, tasks, workdirNone, **kwargs): # 这里可以调用 yolo obb train 命令或者做一个队列任务 return { status: training not configured in this backend }我个人更推荐训练流程外置。模型训练涉及数据集校验、yaml 生成、超参调整这些放在标注平台里反而不好排查问题。训练 OBB 模型本身建议用yolo obb train dataxxx.yaml modelyolov8s-obb.pt epochs100这类命令训练脚本和 ML 后端的代码分开维护避免逻辑耦合。训练好后把权重文件路径更新到 Model.py 的初始化参数里或者挂到共享存储避免每次重新提交模型。4. 部署、验证与常见问题速查4.1 启动与接入 Label Studio启动 ML 后端服务label-studio-ml start ./yolo_obb_backend --port 9090然后在 Label Studio 项目设置里Add Model填入http://127.0.0.1:9090保存后模型会显示为 connected。接着新建标注任务打开任意一张图就能看到预标注框自动出现。如果看不到优先检查浏览器 console 和 ML 后端日志。调试技巧先在本地跑一个 mini 脚本手动构造一个 tasks 列表调用 predict 方法。这样不用启动服务就能排出大部分问题。from yolo_obb_backend import YOLOV8OBBModel model YOLOV8OBBModel(model_pathbest.pt) tasks [{data: {image: /path/to/test.jpg}, id: test}] out model.predict(tasks) print(out.predictions[0][result][0][value])这个调试脚本能省掉不少来回启动服务的等待时间建议写进项目仓库每次换权重都跑一遍。4.2 线上图片和本地路径的兼容ML 后端有两种常见部署位置一种和 Label Studio 在同一台机器直接用本地路径另一种是独立服务器Label Studio 在另一台机上传来的 image 是一个 http URL。用get_local_path可以统一处理。但要留意如果 Label Studio 的图片 URL 带临时签名缓存过期后可能下载失败需要保证 ML 后端所在服务能正常访问这个 URL。如果经常出问题最稳妥的做法是把图片全部转成本地路径或者在 Label Studio 中开启本地存储让标注平台直接把图片放在共享盘上。4.3 角度、坐标、标签映射等典型坑这里整理了一个速查表都是实际项目里容易踩的问题问题现象原因解决预测框不在图上框坐标非常大或超出画布像素坐标当百分制坐标送过去了统一除以宽高再乘 100预测框方向反了框的朝向和实际目标反向y 轴方向或旋转方向理解不一致用 OpenCV 画框可视化验证必要时将 angle 取负预估框正确但没标签框是空的或者不出from_name 和标注配置里 PolygonLabels 不一致检查标注 XML 的 name 字段是否一致标签名称对不上类别错乱model.names 和 Label Studio 标签顺序不一致训练后打印 model.names先同步标注配置单张图推理很慢每张图要好几秒推理落在 CPU 上显式指定 device0或换小模型前端出现交叉框四边形自相交4 个角点顺序不对角点按顺时针或逆时针统一排序关于角度方向我再多啰嗦一句。YOLO OBB 的angle是弧度制逆时针为正但屏幕坐标系 y 向下所以按数学公式计算后视觉上可能是顺时针。如果发现框的方向和预期相反把angle取负再旋转或者对 y 坐标做反转后旋转。我在实际项目中用img_h - y配合数学坐标系旋转避免直角坐标混乱。务必先做可视化验证再部署。关于置信度预标注场景如果目标很小建议conf调低到 0.15 到 0.25。宁可多标几个候选让标注员删掉也不要漏掉目标后再去手动画。漏标的修正成本往往比删框高得多。4.4 标注效率的进一步优化预标注接入成功后我建议再做两件事。一是把模型推理的imgsz和conf做成可配置项通过环境变量或者配置文件暴露出来方便不同任务切换。二是对于小目标占比高的数据考虑切片推理把大图切成带重叠的图块每个图块单独预测再按坐标合并回原图OBB 模型的召回率会明显提高。最后分享一个我自己的习惯Model.py 写完之后我会在代码里保留一个--debug运行模式直接用 OpenCV 或 PIL 把预测框画出来存成文件每次更换权重都必须肉眼抽查几十张。这个习惯帮我躲避了至少三次“角度翻转”和“坐标错位”的坑。自动化测试可以保证代码不出错但标注预览的视觉效果必须靠人来确认别嫌麻烦这步省不掉。本文还有配套的精品资源点击获取
返回列表