简介:2022-RoLabelImg 是一款面向计算机视觉与机器学习研发者的图像标注工具,尤其适合从事目标检测、自动驾驶等方向的研究人员与学生使用。该版本针对 Windows 环境做了专门优化,修复了以往安装与运行中可能出现的兼容性故障,解压后即可直接使用,省去繁琐配置。资源包共 121 个文件,约 32.62MB,以 png 界面截图、py 源码脚本、pyc 编译文件、sample 示例数据及 svg 图标为主,另含 xml 配置、sh 脚本与 rst 说明文档,结构完整便于二次开发与查阅。目前已有 779 人学习下载。工具提供直观图形界面,支持矩形、多边形、圆形及点线等多种标注方式,可批量加载图像提升效率,并支持导出 PASCAL VOC XML、YOLO YAML、COCO JSON 等主流格式,方便对接 TensorFlow、PyTorch 等框架。其版本控制与自定义快捷键功能,能有效避免标注进度丢失并加快操作速度,是图像标注任务中值得信赖的实用选择。
1. 旋转框标注这件事,为什么 2022 年还有人回头翻 RoLabelImg
如果你做过遥感影像、工业质检或者文档版面分析,大概率遇到过这样的场景:目标本身是斜的,用水平框标注时框里塞满了背景,模型学到的特征一半是噪声。2022 年我在做一个遥感舰船检测项目时就卡在这里,水平框的 mAP 死活上不去,换成旋转框后同样的 backbone 直接涨了十几个点。问题随之而来——旋转框怎么标?labelImg 只能画水平框,coco-annotator 部署又太重,翻来翻去,RoLabelImg 这个名字反复出现在搜索结果里。
RoLabelImg 是 labelImg 的一个分支,核心改动是支持旋转矩形框(rotated bounding box)标注,输出格式兼容 PASCAL VOC 的 XML,额外记录了旋转角度。它解决的就是「斜目标标注」这一件事,适合做遥感、文本检测、工业缺陷检测的从业者。这篇不是科普,我会把安装、标注、格式转换、训练对接和踩过的坑一次讲清楚,让你看完就能在自己机器上跑通。
2. RoLabelImg 的旋转框到底怎么表示:角度、方向和坐标系
2.1 旋转框的两种主流表示法
在动手之前必须搞清楚一件事:旋转框在数据里到底长什么样。目前主流有两种表示法,RoLabelImg 用的是第一种。
第一种是「中心点 + 宽高 + 角度」,记作 (cx, cy, w, h, θ)。cx、cy 是框中心坐标,w 是框的宽,h 是框的高,θ 是旋转角度。这种表示法在 OpenCV 的 minAreaRect 和 mmrotate 里都是默认格式,好处是角度和尺寸解耦,回归时比较稳定。
第二种是「四角点坐标」,直接存 (x1,y1,x2,y2,x3,y3,x4,y4)。这种表示法没有角度歧义,但网络回归时四个点互相耦合,训练容易抖。RoLabelImg 的 XML 里其实两种信息都有——它存了中心点和角度,同时也能反算出四个角点。
RoLabelImg 的 XML 结构大致是这样:
<annotation> <folder>images</folder> <filename>ship_001.jpg</filename> <size> <width>1024</width> <height>1024</height> <depth>3</depth> </size> <object> <name>ship</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <robndbox> <cx>512.0</cx> <cy>480.0</cy> <w>120.0</w> <h>40.0</h> <angle>1.5708</angle> </robndbox> </object> </annotation>注意这里的关键点:robndbox节点替代了普通 labelImg 的bndbox,角度angle用的是弧度制,不是角度制。这一点后面避坑章节会重点讲。
2.2 角度定义:最容易翻车的地方
角度这件事,不同工具的定义能差出 90 度甚至 180 度。RoLabelImg 的角度定义是:以框的中心为原点,w 边(宽边)与水平方向的夹角,逆时针为正,范围是 [0, π)。也就是说角度只在 0 到 180 度之间,不会出现负角度或者超过 180 度的情况。
为什么范围是 [0, π) 而不是 [0, 2π)?因为一个矩形旋转 180 度和旋转 0 度在几何上是同一个框,没必要区分。这个约定和 OpenCV 的 minAreaRect 一致,但和某些论文里用 [0, 2π) 的定义不同。如果你拿 RoLabelImg 标的数据去喂一个假设角度范围是 [0, 2π) 的模型,训练会直接崩。
我一般会在标注前先确认三件事:角度单位是弧度还是角度、角度范围是 [0,π) 还是 [0,2π)、角度零度对应的是水平还是垂直。这三个问题问清楚,能省掉后面几天的调试时间。
2.3 从 XML 反算四角点的公式
很多时候训练框架要的是四角点,需要从 RoLabelImg 的 XML 转换。转换公式如下:
import math def robndbox_to_corners(cx, cy, w, h, angle): """ 将 RoLabelImg 的中心点+宽高+角度转换为四角点 cx, cy: 中心点坐标 w, h: 框的宽和高 angle: 弧度制,逆时针为正 返回: [(x1,y1), (x2,y2), (x3,y3), (x4,y4)] """ # 计算宽高的一半 dx = w / 2.0 dy = h / 2.0 cos_a = math.cos(angle) sin_a = math.sin(angle) # 四个角点相对于中心的偏移,按逆时针顺序 # 顺序: 右下 -> 右上 -> 左上 -> 左下(取决于坐标系y轴方向) corners = [] for sx, sy in [(dx, dy), (dx, -dy), (-dx, -dy), (-dx, dy)]: # 旋转矩阵作用于偏移量 rx = sx * cos_a - sy * sin_a ry = sx * sin_a + sy * cos_a corners.append((cx + rx, cy + ry)) return corners这段代码的逻辑是:先把四个角点相对于中心的偏移量算出来,然后用旋转矩阵旋转,最后加回中心坐标。参数说明——angle必须是弧度,如果你从 XML 里读出来是弧度就直接用,如果是角度记得先math.radians()转一下。w和h的顺序不能反,RoLabelImg 里 w 是框的长边还是短边取决于你标注时怎么拉的,这个后面也会讲。
提示:转换完的四角点建议用 OpenCV 的
cv2.polylines画出来肉眼核对一遍,尤其是角度接近 0 或 π/2 的框,最容易看出方向对不对。
3. 从零跑通 RoLabelImg:安装、标注、导出全流程
3.1 环境准备与安装
RoLabelImg 是 Python 写的,依赖 PyQt5 和 lxml。我一般用 conda 建一个干净环境,避免和系统里的 Qt 冲突。
# 创建独立环境,Python 版本建议 3.8 到 3.10 conda create -n rolabelimg python=3.9 -y conda activate rolabelimg # 安装依赖 pip install PyQt5 lxml # 从源码运行(假设你已经把仓库 clone 到本地) cd RoLabelImg python rolabelimg.py如果你不想用 conda,用 venv 也行,但要注意 PyQt5 在某些 Linux 发行版上需要额外装系统库,比如libxcb-xinerama0。Windows 用户一般直接 pip 装就能跑,macOS 上如果报 Qt 插件错误,通常是 PyQt5 版本和系统不匹配,换个版本试试。
启动后界面和 labelImg 几乎一样,区别在工具栏上多了几个旋转相关的按钮。左侧是文件列表,中间是画布,右侧是标注框列表。
3.2 标注旋转框的完整操作
标注流程和 labelImg 类似,但有几个关键差异:
第一步,打开图片目录。点「Open Dir」选择你的图片文件夹,再点「Change Save Dir」选 XML 的保存目录。建议图片和 XML 分开放,后面转换脚本好处理。
第二步,切换标注模式。工具栏上有个按钮可以在「水平框」和「旋转框」之间切换,图标是一个带角度的矩形。一定要确认切到旋转框模式,否则画出来的还是普通 bndbox。
第三步,画框。在旋转框模式下,鼠标左键点一下确定框的中心,然后拖动确定大小,松开后再移动鼠标调整角度,最后点一下确认。这个交互和水平框的「对角拖拽」不一样,第一次用会有点不习惯。
第四步,微调。画完之后框的四个角上有控制点,可以拖动调整。角度也可以通过拖动框边缘来改。如果角度差一点点,可以用键盘的方向键微调,具体快捷键在菜单里能查到。
第五步,保存。按 Ctrl+S 保存当前 XML,或者点「Save」按钮。建议每标十几张就批量保存一次,避免软件崩了丢数据。
3.3 批量导出与格式检查
标完一批之后,先别急着拿去训练,做一次格式检查。我一般写个小脚本扫一遍所有 XML,确认没有空框、角度越界、坐标超出图片范围这些问题。
import os import xml.etree.ElementTree as ET import math def check_rolabelimg_xml(xml_dir, img_width=1024, img_height=1024): """ 检查 RoLabelImg 导出的 XML 是否有常见问题 """ issues = [] for fname in os.listdir(xml_dir): if not fname.endswith('.xml'): continue path = os.path.join(xml_dir, fname) tree = ET.parse(path) root = tree.getroot() for obj in root.findall('object'): robndbox = obj.find('robndbox') if robndbox is None: issues.append(f"{fname}: 缺少 robndbox 节点") continue cx = float(robndbox.find('cx').text) cy = float(robndbox.find('cy').text) w = float(robndbox.find('w').text) h = float(robndbox.find('h').text) angle = float(robndbox.find('angle').text) # 检查角度范围 if angle < 0 or angle >= math.pi: issues.append(f"{fname}: 角度 {angle} 超出 [0, pi) 范围") # 检查框是否超出图片边界(粗略判断) if cx - w/2 < 0 or cx + w/2 > img_width: issues.append(f"{fname}: 框水平方向可能超出图片") if cy - h/2 < 0 or cy + h/2 > img_height: issues.append(f"{fname}: 框垂直方向可能超出图片") # 检查宽高是否合理 if w <= 0 or h <= 0: issues.append(f"{fname}: 宽或高非正数 w={w}, h={h}") return issues # 使用示例 problems = check_rolabelimg_xml('./annotations') for p in problems: print(p) print(f"共发现 {len(problems)} 个问题")这个脚本检查四类问题:缺 robndbox 节点、角度越界、框超出图片、宽高非正。参数img_width和img_height要按你实际图片尺寸改,如果你的图片尺寸不统一,可以先读图片再传进来。跑完如果输出为空,说明格式没问题,可以进入下一步。
3.4 转成训练框架要的格式
RoLabelImg 的 XML 不能直接喂给 mmrotate 或 YOLO 系列,需要转换。以 mmrotate 为例,它要的是 DOTA 格式的 txt,每行是x1 y1 x2 y2 x3 y3 x4 y4 category difficult。
import os import math import xml.etree.ElementTree as ET def xml_to_dota(xml_dir, out_txt, img_width=1024, img_height=1024): """ 将 RoLabelImg XML 转为 DOTA 格式 txt 每行: x1 y1 x2 y2 x3 y3 x4 y4 category difficult """ with open(out_txt, 'w', encoding='utf-8') as f: for fname in sorted(os.listdir(xml_dir)): if not fname.endswith('.xml'): continue tree = ET.parse(os.path.join(xml_dir, fname)) root = tree.getroot() for obj in root.findall('object'): name = obj.find('name').text robndbox = obj.find('robndbox') cx = float(robndbox.find('cx').text) cy = float(robndbox.find('cy').text) w = float(robndbox.find('w').text) h = float(robndbox.find('h').text) angle = float(robndbox.find('angle').text) # 转四角点 dx, dy = w/2.0, h/2.0 cos_a, sin_a = math.cos(angle), math.sin(angle) corners = [] for sx, sy in [(dx,dy),(dx,-dy),(-dx,-dy),(-dx,dy)]: rx = sx*cos_a - sy*sin_a ry = sx*sin_a + sy*cos_a corners.append((cx+rx, cy+ry)) # 裁剪到图片范围内 coords = [] for x, y in corners: x = max(0, min(img_width, x)) y = max(0, min(img_height, y)) coords.extend([str(int(round(x))), str(int(round(y)))]) line = ' '.join(coords) + f' {name} 0\n' f.write(line) # 使用 xml_to_dota('./annotations', './train_dota.txt')转换时有两个细节要注意:一是坐标要裁剪到图片范围内,否则 mmrotate 读数据时会报越界;二是角点顺序要一致,DOTA 格式默认是顺时针或逆时针,不同版本要求可能不同,转换完最好可视化几张确认。
4. 标注质量与效率:那些影响模型精度的隐藏细节
4.1 角度标注的一致性比精度更重要
做旋转框检测,最怕的不是标得不够准,而是标得不一致。同一类目标,有的标成 0 度,有的标成 90 度,模型根本学不明白。我见过一个项目,两个人标同一批数据,一个人习惯把长边当 w,另一个人习惯把短边当 w,结果角度差了 90 度,训练 loss 一直震荡。
解决办法是定一个标注规范:对于有明确方向的目标(比如舰船有船头船尾),统一以某个方向为 0 度参考;对于没有方向的目标(比如圆形储罐),统一把长边作为 w,角度取 [0, π/2) 范围。规范定好之后,先标 50 张让所有人对齐,确认一致了再批量标。
4.2 边界目标的处理策略
图片边缘的目标怎么标,直接影响模型在边缘的召回。常见做法有三种:一是直接不标,只标完整目标;二是标出来但打上 truncated 标记;三是标出来不管。我一般用第二种,因为遥感图像里边缘目标很多,全丢掉会损失不少正样本。
RoLabelImg 的 XML 里有truncated字段,标的时候在右侧面板勾上就行。训练时可以在 loss 里对 truncated 样本降权,或者单独统计这类样本的召回率。
4.3 用快捷键把标注速度提上去
纯鼠标标注一张图要一两分钟,用快捷键能压到二三十秒。RoLabelImg 支持自定义快捷键,我一般会改这几个:W 创建旋转框、A 上一张、D 下一张、Ctrl+S 保存、Del 删除选中框。具体在菜单「Edit」里能找到快捷键设置。
另外一个小技巧是先把图片按目标密度排序,先标密集的再标稀疏的,因为密集图片标完后面会越标越顺手。这个习惯让我在一个 5000 张的项目里省了大概两天时间。
5. 避坑与排查:RoLabelImg 用起来最容易翻车的 5 个地方
5.1 角度单位搞混,训练 loss 直接爆炸
现象:转换完数据拿去训练,loss 一开始就是几千甚至几万,完全不收敛。
原因:RoLabelImg 的 XML 里 angle 是弧度制,但有些转换脚本或者训练配置默认按角度制处理,导致角度值差了 57 倍。
解决:在转换脚本里统一用弧度,如果训练框架要角度,在最后一步再转。检查方法是打印几个框的 angle 值,正常应该在 0 到 3.14 之间,如果出现 90 这种数就是单位错了。
5.2 宽高顺序反了,框的形状完全不对
现象:可视化转换后的框,发现框的长宽比和标注时完全相反,本来细长的船变成了扁的。
原因:RoLabelImg 里 w 和 h 的定义取决于你画框时先拉哪个方向,不同人标出来的 w/h 顺序可能不一致。转换脚本如果假设 w 一定是长边,就会出错。
解决:在转换时不要假设 w 和 h 的大小关系,直接按 XML 里的值算四角点。如果训练框架要求 w 是长边,在转换时判断一下,如果 w < h 就交换 w 和 h 并把角度加 π/2。
5.3 坐标越界导致数据加载报错
现象:训练时 dataloader 报错,提示坐标超出图片范围,或者可视化时框跑到图片外面。
原因:标注时框的边缘可能超出图片边界,RoLabelImg 不会自动裁剪,导出的坐标就是负的或者大于图片宽高。
解决:在转换脚本里加裁剪逻辑,把所有坐标 clamp 到 [0, width] 和 [0, height]。上面 3.4 节的脚本里已经加了这一步,直接用就行。
5.4 中文路径导致 XML 读取失败
现象:脚本读 XML 时报编码错误,或者读出来的中文类别名是乱码。
原因:Windows 下路径含中文时,某些 Python 版本的 XML 解析器默认编码不是 UTF-8。
解决:读文件时显式指定编码,ET.parse(path)改成先open(path, 'r', encoding='utf-8')再ET.parse。另外图片路径也尽量用英文,避免不必要的麻烦。
5.5 标完忘记保存,软件崩溃全白干
现象:标了一下午,软件突然闪退,重新打开发现 XML 还是旧的。
原因:RoLabelImg 默认不会自动保存,只有手动按 Ctrl+S 才写盘。
解决:养成每标 10 到 20 张就按一次 Ctrl+S 的习惯。另外可以定期把 annotations 目录备份一份,我一般用rsync或者简单的cp -r定时跑。
6. 把 RoLabelImg 数据接进 mmrotate:一个可复现的最小训练配置
标注只是第一步,真正验证数据质量的是能不能训起来。这里给一个 mmrotate 的最小配置,用前面转好的 DOTA 格式数据跑通训练。
首先是数据目录结构,按 mmrotate 的要求组织:
data/split_ss_dota/ ├── trainval/ │ ├── images/ # 所有训练图片 │ └── annfiles/ # 对应的 DOTA txt └── test/ ├── images/ └── annfiles/然后是配置文件的关键部分:
# configs/rotated_retinanet/rotated_retinanet_r50_fpn_1x_dota.py 的核心改动 dataset_type = 'DOTADataset' data_root = 'data/split_ss_dota/' img_norm_cfg = dict( mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations', with_bbox=True), dict(type='RResize', img_scale=(1024, 1024)), dict(type='RRandomFlip', flip_ratio=0.5), dict(type='Normalize', **img_norm_cfg), dict(type='Pad', size_divisor=32), dict(type='DefaultFormatBundle'), dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), ] data = dict( samples_per_gpu=2, workers_per_gpu=2, train=dict( type=dataset_type, ann_file=data_root + 'trainval/annfiles/', img_prefix=data_root + 'trainval/images/', pipeline=train_pipeline), val=dict( type=dataset_type, ann_file=data_root + 'test/annfiles/', img_prefix=data_root + 'test/images/', pipeline=test_pipeline), test=dict( type=dataset_type, ann_file=data_root + 'test/annfiles/', img_prefix=data_root + 'test/images/', pipeline=test_pipeline))几个关键参数说明:img_scale设成 1024 是因为遥感图片通常比较大,太小会丢小目标;samples_per_gpu=2是显存不够时的保守值,显存够可以加到 4;RRandomFlip是旋转框专用的翻转增强,比普通 flip 多了角度处理。
启动训练:
# 单卡训练 python tools/train.py configs/rotated_retinanet/rotated_retinanet_r50_fpn_1x_dota.py # 多卡训练 bash tools/dist_train.sh configs/rotated_retinanet/rotated_retinanet_r50_fpn_1x_dota.py 4训练起来之后,重点看两个指标:loss 是否稳定下降,以及验证集的可视化结果框的方向对不对。如果 loss 降但可视化框方向乱,大概率是角度定义和模型假设不一致,回头检查 2.2 节说的那三个问题。
验证数据质量还有一个土办法:拿训练好的模型在训练集上推理,如果训练集上框都画不准,那一定是标注或转换有问题,不用怀疑模型。这个习惯帮我省了很多次瞎调参的时间。
最后说个我自己的教训:早期做旋转框项目时,我总觉得标注是脏活累活,随便标标就行,结果模型效果一直上不去,回头查数据发现 30% 的框角度都是错的。后来我定了个规矩——标注规范先写清楚,标完先抽检 10%,确认没问题再批量。这个习惯比任何调参技巧都管用。希望帮到你。
本文还有配套的精品资源,点击获取