news 2026/10/3 9:06:01

2022-RoLabelImg旋转框标注实战:从环境搭建到YOLO-OBB格式转换

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
2022-RoLabelImg旋转框标注实战:从环境搭建到YOLO-OBB格式转换

简介:2022-RoLabelImg 是一款面向计算机视觉与机器学习研发者的图像标注工具,尤其适合从事目标检测、自动驾驶等方向的研究人员与学生使用。该版本针对 Windows 环境做了专门优化,修复了以往安装与运行中可能出现的兼容性故障,解压后即可直接使用,省去繁琐的配置环节。资源包共收录 121 个文件,整体约 32.62MB,以 png 界面截图、py 源码脚本、pyc 编译文件、sample 示例数据及 svg 图标为主,另含 sh 脚本、xml 配置与 rst 说明文档,目录结构完整,便于二次开发与功能查阅。工具支持矩形、多边形、圆形、点与线等多种标注形式,可批量加载图像,并导出 PASCAL VOC XML、YOLO YAML、COCO JSON 等主流格式,兼容 TensorFlow、PyTorch 等框架,同时提供进度保存恢复与自定义快捷键。目前已有 779 人学习下载,适合需要高效完成数据标注、加速项目迭代的开发者参考使用。

1. 旋转框标注的刚需:2022-RoLabelImg 到底解决了什么

做遥感影像目标检测的同行大概率都经历过这个场景:一张航拍图里停着几十架飞机,每架飞机的朝向都不一样,用水平矩形框去标,框里塞满了背景像素,模型学出来的定位精度怎么调都上不去。2022-RoLabelImg 就是冲着这个痛点来的——它是经典标注工具 LabelImg 的一个旋转框分支版本,允许你画出带角度的矩形框(OBB,Oriented Bounding Box),把目标的朝向信息一并标进去。如果你手头的数据集是 DOTA、HRSC2016、UCAS-AOD 这类遥感或航拍数据,或者工业质检里需要标注斜向排列的零件,这个工具值得花半小时搭起来。它不依赖 GPU,Python 环境跑起来就能用,标注结果直接输出 XML,和 PASCAL VOC 格式兼容,后续转 YOLO-OBB 或 DOTA 格式都有成熟脚本。适合谁?做旋转目标检测的数据标注员、算法工程师、以及需要快速验证 OBB 方案可行性的小团队。

2. 把 2022-RoLabelImg 跑起来:环境、依赖与最小启动命令

2.1 为什么选它而不是 LabelImg 原版或 CVAT

LabelImg 原版只支持水平框,这是硬伤。CVAT 功能全但部署重,标注旋转框的交互路径长,小团队用起来时间成本高。2022-RoLabelImg 的定位很清晰:在 LabelImg 的轻量交互基础上,加了旋转框的绘制、调整和序列化。它的标注文件里会多出<robndbox>节点,包含cx、cy、w、h、angle五个参数,角度单位是弧度,范围在[-π/2, π/2]之间。这个格式后来被不少 OBB 检测框架直接读取,省去了自己写解析器的麻烦。

选型时要注意:这个分支版本在 2022 年前后有若干次更新,不同 commit 对 Python 版本和 PyQt5 的兼容性有差异。我一般会锁定 Python 3.8 或 3.9,PyQt5 用 5.15.x,避免踩到 PyQt6 的 API 断裂。

2.2 从零搭建:四条命令跑通标注界面

假设你用的是 Ubuntu 20.04 或 Windows 10 以上,Python 环境已经就绪。下面是我在本地和服务器上都验证过的最小启动流程:

# 1. 创建独立环境,避免和系统 PyQt 冲突 conda create -n rolabel python=3.8 -y conda activate rolabel # 2. 安装核心依赖,PyQt5 指定 5.15 系列 pip install PyQt5==5.15.9 lxml # 3. 获取 2022-RoLabelImg 源码(从你手头的仓库或镜像) git clone <你的仓库地址> RoLabelImg cd RoLabelImg # 4. 启动标注工具,指定图片目录和预定义类别 python labelImg.py ./images ./classes.txt

启动后界面和 LabelImg 几乎一样,区别在工具栏多了一个旋转框按钮。快捷键W画水平框,E画旋转框,画完后拖动四个角点可以调整角度和尺寸。classes.txt里一行一个类别名,比如plane、ship、vehicle。

参数说明:labelImg.py第一个参数是图片文件夹路径,第二个是类别文件。如果图片和标注要在不同目录,用--save_dir指定 XML 输出位置。默认保存格式是 PASCAL VOC,但旋转框会写成<robndbox>,不是<bndbox>。这一点在后续写数据加载器时要特别注意。

2.3 标注文件结构:robndbox 五个参数怎么读

画完一个旋转框,XML 里长这样:

<object> <name>plane</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <robndbox> <cx>512.3</cx> <cy>384.7</cy> <w>120.5</w> <h>45.2</h> <angle>0.785</angle> </robndbox> </object>

cx、cy是旋转框中心点像素坐标,w是长边,h是短边,angle是长边与水平轴的夹角,弧度制。这里有个容易翻车的点:不同版本的 RoLabelImg 对 angle 的定义可能差一个 π/2 的偏移,有的以长边为基准,有的以 x 轴正方向为基准。拿到标注后第一件事是可视化验证,别直接喂给模型。

3. 从 XML 到训练格式:旋转框转换的四个边界坑

3.1 转 DOTA 格式:四角点顺序决定成败

DOTA 格式一行是x1 y1 x2 y2 x3 y3 x4 y4 category difficult,四个点按顺时针或逆时针排列,不能交叉。从robndbox转四角点的公式:

import math def robndbox_to_dota(cx, cy, w, h, angle): # 计算未旋转时的四个角点(相对于中心) dx = w / 2 dy = h / 2 corners = [(-dx, -dy), (dx, -dy), (dx, dy), (-dx, dy)] # 旋转矩阵 cos_a = math.cos(angle) sin_a = math.sin(angle) points = [] for x, y in corners: rx = x * cos_a - y * sin_a + cx ry = x * sin_a + y * cos_a + cy points.append((rx, ry)) # 按顺时针输出,从左上角开始(近似) return points

逻辑说明:先把旋转框还原成以中心为原点的水平矩形,再套旋转矩阵,最后平移回图像坐标系。参数angle直接取自 XML,但如果你发现可视化时框的方向反了,大概率是 angle 的正负号约定不一致,取反即可。输出顺序建议统一为顺时针,从最靠近左上角的点开始,这样后续转 YOLO-OBB 时不用再排序。

3.2 转 YOLO-OBB:归一化和角度范围裁剪

YOLO-OBB 的标签格式是class_index cx cy w h angle,全部归一化到[0,1],角度通常要求[0, π/2)。转换时有两个坑:一是w和h要除以图像宽高,但w对应的是长边,归一化时分别除以宽和高;二是角度如果落在[-π/2, 0),要加 π/2 转到正值区间。

def dota_to_yolo_obb(points, img_w, img_h, class_idx): xs = [p[0] for p in points] ys = [p[1] for p in points] cx = sum(xs) / 4 / img_w cy = sum(ys) / 4 / img_h # 计算旋转框的宽高和角度 edge1 = math.hypot(xs[1]-xs[0], ys[1]-ys[0]) edge2 = math.hypot(xs[2]-xs[1], ys[2]-ys[1]) w = max(edge1, edge2) / img_w h = min(edge1, edge2) / img_h angle = math.atan2(ys[1]-ys[0], xs[1]-xs[0]) if angle < 0: angle += math.pi / 2 if angle >= math.pi / 2: angle -= math.pi / 2 return f"{class_idx} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f} {angle:.6f}"

参数说明:points是上一步输出的四角点,img_w、img_h从图片读取。角度归一化到[0, π/2)是多数 YOLO-OBB 实现的要求,但如果你用的框架文档写了别的范围,以框架为准。这个函数我一般会跑一遍全量数据,统计角度分布,确认没有异常值。

3.3 批量转换脚本:多进程加速与错误日志

几千张图用单进程转太慢,我一般用multiprocessing.Pool并行:

import os import glob from multiprocessing import Pool from xml.etree import ElementTree as ET def convert_one(xml_path): try: tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find('size/width').text) img_h = int(root.find('size/height').text) lines = [] for obj in root.findall('object'): name = obj.find('name').text rb = obj.find('robndbox') if rb is None: continue cx = float(rb.find('cx').text) cy = float(rb.find('cy').text) w = float(rb.find('w').text) h = float(rb.find('h').text) angle = float(rb.find('angle').text) points = robndbox_to_dota(cx, cy, w, h, angle) line = dota_to_yolo_obb(points, img_w, img_h, class_map[name]) lines.append(line) out_path = xml_path.replace('.xml', '.txt').replace('annotations', 'labels') with open(out_path, 'w') as f: f.write('\n'.join(lines)) except Exception as e: return f"{xml_path}: {str(e)}" return None if __name__ == '__main__': xml_files = glob.glob('./annotations/*.xml') with Pool(8) as p: errors = p.map(convert_one, xml_files) for err in errors: if err: print(err)

逻辑说明:每个进程独立解析一个 XML,转换后写到对应的labels目录。错误不中断整体流程,最后统一打印。class_map是类别名到索引的字典,从classes.txt生成。跑完后检查错误日志,常见问题是 XML 里缺少robndbox节点(标注时误用了水平框)或图片尺寸字段缺失。

3.4 可视化验证:别跳过这一步

转完格式后,我习惯用 OpenCV 画一遍框,确认角度和位置没跑偏:

import cv2 import numpy as np def draw_obb(img_path, label_path): img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path) as f: for line in f: parts = line.strip().split() cls, cx, cy, bw, bh, angle = int(parts[0]), *map(float, parts[1:]) cx *= w; cy *= h; bw *= w; bh *= h rect = ((cx, cy), (bw, bh), math.degrees(angle)) box = cv2.boxPoints(rect).astype(np.int32) cv2.drawContours(img, [box], 0, (0, 255, 0), 2) cv2.imwrite('vis.jpg', img)

这一步能抓出 90% 的转换错误:角度偏移、宽高颠倒、归一化除错。别省这几分钟。

4. 避坑与排查:标注和转换中最容易翻车的五件事

4.1 现象:画完旋转框,XML 里只有 bndbox 没有 robndbox

原因:启动时没有切换到旋转框模式,或者快捷键按成了W而不是E。2022-RoLabelImg 默认还是水平框模式,需要手动点工具栏的旋转框图标。

解决:标注前先确认工具栏旋转框按钮处于按下状态。如果已经标了一批,只能重新标,没有后悔药。建议在classes.txt同目录放一个说明文件,提醒标注员先切模式。

4.2 现象:转换后的 YOLO-OBB 标签训练时 loss 不降

原因:角度范围没对齐。你的框架可能要求[-π/4, 3π/4)或[-π/2, π/2),而脚本输出的是[0, π/2)。角度差一个常数,模型学起来就是玄学。

解决:查框架的dataset.py里对 angle 的处理逻辑,把转换脚本的输出范围改成和它一致。最稳妥的办法是拿一张图,手动算一遍期望值,和脚本输出对比。

4.3 现象:可视化时框的位置对,但方向反了

原因:robndbox的 angle 定义和你的旋转矩阵方向不一致。有的版本 angle 是顺时针为正,有的是逆时针。

解决:在robndbox_to_dota里把sin_a取反,重新可视化。如果对了,就在脚本里固定这个符号约定,别每次改。

4.4 现象:批量转换时部分图片报 “width/height not found”

原因:XML 里<size>节点缺失或图片损坏。LabelImg 在某些异常退出情况下会写出不完整的 XML。

解决:在转换脚本里加 try-except,把报错文件路径记下来,单独用xmllint检查。缺失的尺寸可以从对应图片用cv2.imread补读。

4.5 现象:标注界面卡顿,画框延迟高

原因:图片分辨率太高(比如 8000×6000 的遥感图),PyQt 渲染压力大。

解决:标注前把大图切成 1024×1024 的切片,或者用--resize参数在启动时缩放显示。注意缩放只影响显示,不影响标注坐标,XML 里存的还是原图坐标。切图方案更彻底,但切图时要保证目标不被切断,边缘留 overlap。

5. 进阶技巧:用脚本预标注减少一半工作量

纯手工画旋转框,一天标不了几百个。我的习惯是先用一个粗糙的 OBB 模型跑一遍推理,把预测结果写成robndboxXML,再用 2022-RoLabelImg 打开微调。这样标注员只需要拖动角点修正,不用从零画框。

具体做法:用任意 OBB 检测框架(比如基于 YOLOv8-OBB 或 MMrotate 的模型)对训练集推理,输出cx, cy, w, h, angle,然后写一个反向转换脚本,生成和 RoLabelImg 兼容的 XML。注意类别名要和classes.txt一致,置信度低于 0.3 的框直接丢弃,避免干扰。

def yolo_obb_to_robndbox_xml(img_path, detections, classes, out_xml): # detections: list of (cls_idx, cx, cy, w, h, angle) 归一化值 img = cv2.imread(img_path) h, w = img.shape[:2] root = ET.Element('annotation') ET.SubElement(root, 'filename').text = os.path.basename(img_path) size = ET.SubElement(root, 'size') ET.SubElement(size, 'width').text = str(w) ET.SubElement(size, 'height').text = str(h) ET.SubElement(size, 'depth').text = '3' for cls_idx, cx, cy, bw, bh, angle in detections: obj = ET.SubElement(root, 'object') ET.SubElement(obj, 'name').text = classes[cls_idx] rb = ET.SubElement(obj, 'robndbox') ET.SubElement(rb, 'cx').text = str(cx * w) ET.SubElement(rb, 'cy').text = str(cy * h) ET.SubElement(rb, 'w').text = str(bw * w) ET.SubElement(rb, 'h').text = str(bh * h) ET.SubElement(rb, 'angle').text = str(angle) tree = ET.ElementTree(root) tree.write(out_xml)

参数说明:detections里的角度要和 RoLabelImg 的约定一致,不一致就先转换。生成的 XML 直接放到annotations目录,用 RoLabelImg 打开图片目录就能看到预标注框。标注员微调后保存,覆盖原 XML。

这个流程我用了大半年,标注效率大概提升 40% 到 60%,具体取决于预标注模型的精度。模型越准,人工修正越少。但别完全依赖预标注,低置信度的框一定要人工过一遍,否则错误会固化到训练集里。

还有一个细节:预标注 XML 里的angle如果和 RoLabelImg 的显示不一致,打开后框会乱转。我的做法是先用 10 张图做小批量验证,确认角度对齐后再全量生成。这个验证习惯帮我省过好几次返工。

最后说一个我踩过的坑:RoLabelImg 保存 XML 时会覆盖同名文件,如果预标注和人工标注混在一起,容易丢数据。我现在的做法是预标注文件加_auto后缀,人工确认后再重命名覆盖。多一步操作,但安全。

希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 9:05:26

SSM酒店预订系统实战:从数据库建模到框架配置全解析

又到一年毕业季&#xff0c;后台收到最多的消息就是“SSM酒店预订系统”这个题目。说实话&#xff0c;这个选题能火这么多年是有道理的&#xff1a;SSM是Java Web阶段最经典的框架组合&#xff0c;酒店预订的业务场景又足够完整——有用户、有房间、有订单、有状态流转&#xf…

作者头像 李华
网站建设 2026/10/3 9:05:13

二阶锥规划求解主动配电网最优潮流:从模型到代码实战

简介&#xff1a;基于二阶锥规划的IEEE33节点主动配电网最优潮流求解程序&#xff0c;面向电力系统优化方向的科研人员与电气工程研究生&#xff0c;可用于多源协同运行、配电网经济运行等场景的算例复现。程序基于MATLAB平台&#xff0c;调用YALMIP和CPLEX求解器实现二阶锥规划…

作者头像 李华
网站建设 2026/10/3 9:04:53

Kafka再平衡全解析:从触发原因到排查调优实战

如果让我给Kafka生产环境里的“老大难”排个名&#xff0c;rebalance一定排在前三。这个机制的本意是让消费组里的成员自动协调、自动分配分区&#xff0c;听起来非常“智能”&#xff0c;但真到线上&#xff0c;它经常扮演着消息延迟飙升、消费短暂停滞的幕后推手。我见过好几…

作者头像 李华
网站建设 2026/10/3 9:04:20

大数据基础镜像实战:Hadoop、Spark、Hive、Tez、Hue一键搭建

简介&#xff1a;这是一份面向大数据学习与毕设演示的基础镜像组件包&#xff0c;整合Hadoop、Spark、Hive、Tez、Hue及Kafka等常用组件&#xff0c;帮助读者在Docker环境中快速搭建可运行的大数据集群环境。资源共22个文件&#xff0c;压缩包仅34KB&#xff0c;以10个shell脚本…

作者头像 李华
网站建设 2026/10/3 9:03:50

MuJoCo+PPO实战:从环境配置到稳定行走的完整链路

简介&#xff1a;本资源是一份基于PyTorch实现的近端策略优化&#xff08;PPO&#xff09;强化学习算法代码包&#xff0c;专为MuJoCo物理仿真环境设计&#xff0c;支持Ant-v2、Humanoid-v2、Hopper-v2、HalfCheetah-v2等经典连续控制任务&#xff0c;面向强化学习初学者与进阶…

作者头像 李华
网站建设 2026/10/3 9:02:51

Spring Boot网格仓出入库管理系统设计与实现:从业务建模到毕设答辩

1. 项目核心定位&#xff1a;网格仓出入库管理系统到底在解决什么问题 有段时间很多同学私信问我毕设选题的事&#xff0c;说想做仓库管理系统但又不想烂大街&#xff0c;想加点“网格化”“前置仓”的行业味道。我琢磨了一下&#xff0c;Spring Boot 网格仓出入库登记管理&am…

作者头像 李华