ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RoLabelImg旋转框标注与格式转换实战指南

RoLabelImg旋转框标注与格式转换实战指南 简介2022-RoLabelImg 是一款面向计算机视觉与机器学习研发者的图像标注工具尤其针对 Windows 用户做了安装与运行优化解决了以往版本常见的兼容性故障开箱即用。它提供直观的图形界面支持矩形、多边形、圆形、点与线等多种标注方式可批量加载图像、保存与恢复标注进度并导出 PASCAL VOC XML、YOLO YAML、COCO JSON 等主流格式方便对接 TensorFlow、PyTorch 等框架适合自动驾驶、目标检测等项目的标注预处理。资源包共 121 个文件以 36 个 png 界面截图、17 个 py 源码、12 个 pyc 编译文件及 sample、svg、sh、xml 等配置与示例文件为主整体约 32.62MB目录结构完整便于直接运行与二次开发。目前已有 779 人学习下载可帮助读者快速搭建标注环境、理解工具实现并提升标注效率。1. 旋转框标注这件事为什么 RoLabelImg 到现在还有人用如果你做过遥感影像、工业质检或者文档版面分析大概率遇到过这样的场景目标不是水平摆放的而是斜着的——航拍图里的飞机、港口停泊的船只、PCB 板上的倾斜元件、扫描件里歪斜的文字行。这时候用普通的水平矩形框去标框里会塞进大量背景像素训练出来的检测模型要么定位不准要么把相邻目标混在一起。旋转框标注就是来解决这个问题的而 RoLabelImg 正是当年在 labelImg 基础上改出来的、专门支持旋转框的标注工具2022 年前后仍是不少团队的主力选择。它解决的核心诉求很直接在标注阶段就能画出带角度的矩形框导出成带角度信息的标注文件供后续旋转目标检测模型训练使用。适合谁一是手上有一批倾斜目标数据、需要自己标或组织标注的算法工程师二是想复现旋转检测论文、却卡在数据格式上的研究者三是需要给标注团队定规范、定工具的技术负责人。这篇就把 RoLabelImg 的安装、标注、格式转换和踩坑讲清楚让你能直接上手干活。2. RoLabelImg 的旋转框到底怎么表示从安装到画出第一个斜框2.1 旋转框的数学表示与 RoLabelImg 的选型理由在动手之前得先搞清楚旋转框是怎么被描述的否则后面格式转换会一头雾水。常见的旋转框表示有两类一类是 OpenCV 风格的(cx, cy, w, h, angle)即中心点坐标、宽、高、旋转角另一类是四角点表示(x1,y1,x2,y2,x3,y3,x4,y4)。RoLabelImg 内部和它导出的 XML 采用的是前者角度以弧度制存储这一点非常关键——很多人第一次拿到标注文件看到角度是 0.785 这种数以为是 bug其实是 45 度对应的 π/4。为什么选 RoLabelImg 而不是别的在 2022 年那个时间点支持旋转框的标注工具并不多商业工具要么贵要么不开放格式而 RoLabelImg 是开源的、基于 Python 的、能直接改源码适配自己格式的。它的标注逻辑是在 labelImg 基础上加了旋转手柄画完水平框后可以拖动旋转交互上不算丝滑但胜在可控。常见做法是小批量数据用它精标大批量外包时用它定规范。需要提醒的是RoLabelImg 依赖 PyQt5 和 lxmlPython 版本建议 3.63.8太新的 Python 版本比如 3.10 以上在 PyQt5 的 wheel 上容易翻车这是血泪经验。2.2 在本地把 RoLabelImg 跑起来的最小步骤下面这套流程是我在 Ubuntu 和 Windows 上都验证过的按顺序执行即可。先拉代码、建虚拟环境、装依赖# 建议用 conda 建一个干净的 Python 3.8 环境避免污染主环境 conda create -n rolabel python3.8 -y conda activate rolabel # 安装核心依赖注意 PyQt5 版本不要装太新 pip install pyqt55.15.4 lxml # 进入 RoLabelImg 源码目录后启动 python rolabelimg.py启动后如果报No module named libs.resources说明资源文件没编译需要执行# 在源码根目录执行把 .qrc 资源编译成 Python 文件 pyrcc5 -o libs/resources.py resources.qrc逻辑说明RoLabelImg 把图标等资源放在.qrc文件里运行时需要编译成resources.py才能被 import。参数上pyrcc5是 PyQt5 自带的资源编译器如果你装的是 PyQt6命令会变成pyrcc6但 RoLabelImg 本身是按 PyQt5 写的别混用。启动成功后界面和 labelImg 很像区别在于工具栏多了旋转相关的按钮。打开图片目录按w画框画完后框的四个角会出现旋转控制点拖动即可旋转。标注完成后按CtrlS保存默认导出 Pascal VOC 风格的 XML但每个object里会多出angle节点。2.3 标注操作的关键快捷键与保存格式确认标注效率很大程度上取决于快捷键熟不熟。RoLabelImg 里常用的几个w新建框、d下一张、a上一张、CtrlS保存、CtrlR修改当前框的类别。旋转操作没有默认快捷键得用鼠标拖控制点这是它交互上比较别扭的地方标大量数据时手腕会累。保存后的 XML 结构大致如下重点看robndbox节点object nameship/name poseUnspecified/pose truncated0/truncated difficult0/difficult robndbox cx512.0/cx cy300.0/cy w120.0/w h40.0/h angle0.785/angle /robndbox /object参数说明cx、cy是框中心在图像中的像素坐标w、h是框的宽和高注意这里的宽高是旋转前的边长不随角度变化angle是弧度制角度范围通常是[-π/2, π/2)或[0, π)取决于版本转换时要统一。很多人在这里踩坑以为w是水平投影宽度结果算出来的框完全不对。提示标注前先统一角度定义团队里约定好是逆时针还是顺时针为正否则多人标注合并时角度会打架。3. 把 RoLabelImg 的 XML 喂给训练框架格式转换与角度对齐3.1 为什么不能直接把 XML 丢给模型RoLabelImg 导出的 XML 是给人看和存档的但主流旋转检测框架比如基于 DOTA 的那套、或者 MMDetection 里的旋转分支通常要求特定格式常见的有 DOTA 的(x1,y1,x2,y2,x3,y3,x4,y4)八参数文本格式或者 COCO 的扩展 JSON。你如果直接把robndbox的(cx,cy,w,h,angle)塞进去训练脚本要么读不懂要么读进去但角度定义和框架内部不一致导致 loss 不降、框乱飞。所以中间必须做一层转换。转换的核心是把中心点宽高角度通过旋转矩阵还原成四个角点。这一步的数学不复杂但角度方向、坐标系 y 轴朝向这两个细节是翻车重灾区。3.2 用 Python 把 robndbox 转成 DOTA 八参数格式下面这个脚本是我常用的输入是 RoLabelImg 的 XML 目录输出是 DOTA 格式的 txtimport os import math import xml.etree.ElementTree as ET def rotate_point(cx, cy, x, y, angle): # 将点 (x,y) 绕 (cx,cy) 旋转 angle 弧度 dx, dy x - cx, y - cy rx dx * math.cos(angle) - dy * math.sin(angle) ry dx * math.sin(angle) dy * math.cos(angle) return cx rx, cy ry def convert(xml_path, out_path): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text rbox obj.find(robndbox) cx float(rbox.find(cx).text) cy float(rbox.find(cy).text) w float(rbox.find(w).text) h float(rbox.find(h).text) angle float(rbox.find(angle).text) # 未旋转时的四个角点 corners [(cx - w/2, cy - h/2), (cx w/2, cy - h/2), (cx w/2, cy h/2), (cx - w/2, cy h/2)] rotated [rotate_point(cx, cy, x, y, angle) for x, y in corners] coords .join(f{p[0]:.1f} {p[1]:.1f} for p in rotated) lines.append(f{coords} {name} 0) with open(out_path, w) as f: f.write(\n.join(lines)) # 批量处理 xml_dir ./annotations out_dir ./dota_labels os.makedirs(out_dir, exist_okTrue) for fn in os.listdir(xml_dir): if fn.endswith(.xml): convert(os.path.join(xml_dir, fn), os.path.join(out_dir, fn.replace(.xml, .txt)))逻辑说明先根据中心点和宽高算出未旋转时的四个角点再对每个角点绕中心旋转angle弧度。参数上angle直接取自 XML不做符号翻转——但这里有个前提你的 RoLabelImg 版本和训练框架对角度正方向的定义一致。如果发现框整体镜像或旋转了 90 度八成是 y 轴朝向或角度符号的问题把angle改成-angle再试。注意DOTA 格式要求角点顺序是顺时针或逆时针固定顺序错了 IoU 计算会出问题。上面代码用的是左上→右上→右下→左下转换后最好可视化几张确认。3.3 转换后必须做的可视化验证转换完不验证等于埋雷。我一般会写个小脚本把 DOTA 格式的框画回图上肉眼扫一遍import cv2 import numpy as np def draw_dota(img_path, label_path): img cv2.imread(img_path) with open(label_path) as f: for line in f: parts line.strip().split() coords list(map(float, parts[:8])) pts np.array(coords).reshape(4, 2).astype(np.int32) cv2.polylines(img, [pts], True, (0, 0, 255), 2) cv2.imwrite(check.jpg, img) draw_dota(./images/001.jpg, ./dota_labels/001.txt)参数说明reshape(4,2)把八个坐标还原成四个点polylines的第三个参数True表示闭合。跑完打开check.jpg如果框和目标贴合说明转换正确如果框歪了或者大小不对回去查角度和宽高。4. 标注与转换环节的避坑清单5 个真实踩过的坑4.1 坑一角度范围不统一导致框翻转现象转换后的框看起来旋转了 180 度或者宽高对调了。原因不同版本的 RoLabelImg 对angle的取值范围定义不同有的是[-π/2, π/2)有的是[0, π)还有的把宽高在超过 90 度时互换。解决先标一张已知角度的图导出后看angle数值确认范围转换脚本里加一段归一化逻辑把角度统一到[-π/4, π/4)或你框架要求的区间。4.2 坑二PyQt5 版本过高导致启动崩溃现象执行python rolabelimg.py后报TypeError或直接闪退。原因PyQt5 5.15 之后的某些小版本改了信号槽的接口RoLabelImg 老代码没适配。解决锁死pyqt55.15.4别用pip install pyqt5装最新版。如果已经装了新版先pip uninstall pyqt5再装指定版本。4.3 坑三中文路径导致图片加载失败现象打开目录后图片列表是空的或者缩略图显示不出来。原因RoLabelImg 底层用的一些库对非 ASCII 路径支持不好。解决把图片和标注目录都改成纯英文路径这是最省事的办法如果非要中文路径得改源码里的文件读取部分不推荐。4.4 坑四多人标注角度基准不一致现象两个人标同一批图合并后发现同一类目标的框角度差很多。原因有人以水平向右为 0 度逆时针为正有人以垂直向上为 0 度。解决标注前出一份规范文档明确角度 0 度方向、正方向、取值范围并让每个人先标 5 张样图交叉检查确认一致后再批量开工。4.5 坑五XML 里 robndbox 和 bndbox 混用现象转换脚本报NoneType错误找不到robndbox节点。原因RoLabelImg 在某些操作下会同时写出bndbox和robndbox或者旧版本只写bndbox。解决转换脚本里加判断优先读robndbox没有就跳过或从bndbox补角度为 0同时统一工具版本别混用。5. 进阶批量校验标注质量与角度分布的一个实用技巧标完一批数据怎么快速判断质量我的习惯是统计角度分布并抽样可视化。如果某一类目标的角度全挤在 0 附近要么是目标确实都水平要么是标注员偷懒没转框。下面这段脚本能同时输出角度直方图和随机抽样图import os import math import random import xml.etree.ElementTree as ET import matplotlib.pyplot as plt angles [] xml_dir ./annotations for fn in os.listdir(xml_dir): if not fn.endswith(.xml): continue root ET.parse(os.path.join(xml_dir, fn)).getroot() for obj in root.findall(object): rbox obj.find(robndbox) if rbox is not None: angles.append(float(rbox.find(angle).text)) # 角度直方图看分布是否合理 plt.hist([math.degrees(a) for a in angles], bins36) plt.xlabel(angle (degree)) plt.savefig(angle_dist.png) # 随机抽 10 张做可视化复核 samples random.sample([f for f in os.listdir(xml_dir) if f.endswith(.xml)], 10) print(抽查文件, samples)参数说明bins36表示每 10 度一个区间能看出角度集中在哪些范围math.degrees把弧度转成角度方便看。抽查的 10 张建议人工再过一遍重点看边界目标和密集区域。几个经验值供参考如果做遥感舰船检测角度分布通常比较分散集中在 0 度反而可疑如果做文档版面文字行角度一般在小范围内波动出现大量 90 度附近的值要确认是不是把竖排文字标成了横排旋转。另外RoLabelImg 本身没有内置的质量校验所以这套统计抽查的流程最好固化成脚本每次交付前跑一遍。我现在的习惯是标注团队交数据我先跑角度分布再看抽查图两个都过了才进训练流程。这个习惯帮我挡掉过好几次批量标错的事故比训完发现 mAP 上不去再回头查省太多时间。如果你正准备用 RoLabelImg 起一个旋转检测的数据集建议先把角度定义和转换脚本这两件事定死再让标注铺开不然后期返工的成本远大于前期多花的那半天。希望帮到你。本文还有配套的精品资源点击获取
返回列表