每个图像文件对应保存为独立的 JSON 标注文件,之后运 用代码将 JSON 格式文件转换为适配 YOLO-OBB 检测所需的 txt 格式文件。
时间: 2025-07-23 07:54:11 AIGC 浏览: 34
<think>我们已知YOLO-OBB(Oriented Bounding Box)的标注格式需要每个对象使用一个多边形的表示,通常是一个旋转矩形,由四个角点坐标或中心点、宽高和旋转角度表示。但YOLO-OBB的具体格式可能因实现而异。根据常见实践,YOLO-OBB通常采用以下格式之一:
1. 多边形点集格式:每个对象一行,格式为:`class_id x1 y1 x2 y2 x3 y3 x4 y4`,其中坐标是归一化的(相对于图像宽度和高度),并且是四个角点的坐标,顺序通常为顺时针或逆时针。
2. 另一种常见格式是使用中心点、宽高和旋转角度:`class_id cx cy w h angle`,同样需要归一化。
但是,请注意,Ultralytics官方YOLOv8目前(截至2024年)主要支持水平矩形框(YOLO格式)和分割多边形,对于旋转矩形(OBB)的支持可能通过扩展实现。因此,我们需要确认目标格式。
根据问题描述,用户希望转换为YOLO-OBB所需的txt格式。我们假设采用多边形点集格式(四个角点)且归一化。
步骤:
1. 读取每个JSON文件,其中包含每个对象的标注信息。
2. 对于每个对象,获取其类别索引(需要将类别名称映射到整数索引)和边界框的表示(通常是旋转矩形的四个角点坐标)。
3. 将四个角点的坐标从绝对坐标(像素坐标)转换为归一化坐标(除以图像宽度和高度)。
4. 按照顺序将四个角点的坐标依次写入一行,并在开头加上类别索引。
5. 每个图像对应一个txt文件,文件名与图像文件名相同(仅扩展名不同)。
注意:归一化坐标计算:`x_normalized = x / image_width`, `y_normalized = y / image_height`
假设JSON结构可能如下(具体结构可能因标注工具而异,这里以常见格式为例):
```json
{
"image_width": 1024,
"image_height": 768,
"objects": [
{
"category": "car",
"bbox": [x1, y1, x2, y2, x3, y3, x4, y4] // 四个角点的坐标
},
// ... 其他对象
]
}
```
或者,有时旋转矩形可能用中心点、宽高和角度表示,那么我们需要将其转换为四个角点。但问题中用户没有提供JSON的具体结构,所以我们需要根据实际情况调整。
由于用户没有提供JSON的具体结构,这里我们假设JSON中已经直接提供了四个角点的坐标(按顺序)。
转换步骤示例(代码):
1. 遍历每个JSON文件。
2. 读取图像宽高和对象列表。
3. 对于每个对象,获取类别名,并映射为整数类别索引(需要用户提供类别映射表,例如:{"car":0, "person":1, ...})。
4. 获取该对象的四个角点坐标(假设顺序为[x1,y1, x2,y2, x3,y3, x4,y4])。
5. 归一化坐标:将每个x除以图像宽度,每个y除以图像高度。
6. 将归一化后的坐标按顺序写入一行:`class_id x1 y1 x2 y2 x3 y3 x4 y4`
7. 每个对象一行,写入与图像同名的txt文件中。
注意:如果JSON中的bbox不是四个角点,而是旋转矩形的其他表示(如中心点、宽高、角度),则需要先计算四个角点。
下面是一个Python代码示例,假设JSON结构如上所述(直接提供四个角点):
```python
import json
import os
# 类别映射表,根据实际情况修改
class_map = {
"car": 0,
"person": 1,
# ... 其他类别
}
# 假设JSON文件所在的目录
json_dir = "/path/to/json_dir"
# 输出txt文件的目录
txt_dir = "/path/to/txt_dir"
# 确保输出目录存在
os.makedirs(txt_dir, exist_ok=True)
# 遍历JSON目录
for json_file in os.listdir(json_dir):
if not json_file.endswith('.json'):
continue
# 读取JSON文件
with open(os.path.join(json_dir, json_file), 'r') as f:
data = json.load(f)
image_width = data['image_width']
image_height = data['image_height']
objects = data['objects']
# 准备写入txt文件
txt_filename = os.path.splitext(json_file)[0] + '.txt'
with open(os.path.join(txt_dir, txt_filename), 'w') as f_out:
for obj in objects:
category = obj['category']
if category not in class_map:
continue # 或者跳过,或者报错
class_id = class_map[category]
# 假设bbox是四个角点的列表,顺序为[x1,y1, x2,y2, x3,y3, x4,y4]
bbox = obj['bbox'] # 这是一个包含8个数字的列表
# 归一化
normalized_bbox = []
for i in range(0, len(bbox), 2):
x = bbox[i] / image_width
y = bbox[i+1] / image_height
normalized_bbox.append(x)
normalized_bbox.append(y)
# 写入一行:类别id和8个归一化坐标
line = f"{class_id} " + " ".join([f"{coord:.6f}" for coord in normalized_bbox]) + "\n"
f_out.write(line)
print("转换完成!")
```
如果JSON中的bbox是旋转矩形的其他表示形式,例如中心点(cx,cy)、宽度(w)、高度(h)和角度(angle),那么我们需要将其转换为四个角点。转换方法如下:
1. 计算旋转矩形的四个角点(在图像坐标系中):
- 首先,计算未旋转的矩形四个角点(相对于中心点):
- 左上:(-w/2, -h/2)
- 右上:(w/2, -h/2)
- 右下:(w/2, h/2)
- 左下:(-w/2, h/2)
- 然后,根据角度(弧度)旋转这些点(绕中心点旋转):
- 旋转公式:
$$ x' = x \cdot \cos(\theta) - y \cdot \sin(\theta) $$
$$ y' = x \cdot \sin(\theta) + y \cdot \cos(\theta) $$
- 最后,将旋转后的点加上中心点坐标,得到绝对坐标。
2. 然后归一化。
因此,如果JSON中是这种格式,我们需要在代码中增加转换步骤。
由于用户没有提供JSON的具体结构,我们提供了两种情况的思路。用户需要根据自己JSON的实际结构调整代码。
另外,注意角度单位:有些标注工具使用角度制,有些使用弧度制,需要统一。通常旋转角度在数学公式中使用弧度制,但标注工具可能使用角度制(如DOTA数据集使用角度制,且角度定义为从水平轴(x轴)逆时针旋转)。所以转换时要注意。
如果用户提供的是DOTA格式的标注(使用四个角点),那么可以直接使用上面的代码(因为DOTA的标注就是四个角点的绝对坐标)。
总结:用户需要明确自己的JSON结构,然后调整代码以正确读取边界框信息,并转换为四个归一化的角点坐标。
相关问题:
1. 如何将旋转矩形(中心点、宽高、角度)转换为四个角点坐标?
2. 如果JSON中的角度是角度制,如何转换为弧度制?
3. YOLO-OBB的标注格式中,四个角点的顺序有什么要求?
4. 如何处理不同标注工具生成的JSON格式差异?
5. 如果图像中没有对象,是否需要生成空的txt文件?</think>要将JSON格式的图像标注文件转换为适配YOLO-OBB(Oriented Bounding Box)的TXT格式文件,需遵循以下步骤和格式规范:
### 一、YOLO-OBB标注格式要求
每个对象对应一行数据,格式为:
`class_id x1 y1 x2 y2 x3 y3 x4 y4`
其中:
- `class_id`:物体类别索引(整数)
- `x1 y1 ... x4 y4`:**归一化**的四边形角点坐标(4个点,共8个值)
- 坐标值需按**顺时针或逆时针顺序**排列(需统一)
- 所有坐标值需归一化到 `[0,1]` 范围:
$$ x_{\text{norm}} = \frac{x_{\text{pixel}}}{ \text{image\_width} } , \quad y_{\text{norm}} = \frac{y_{\text{pixel}}}{ \text{image\_height} } $$
### 二、转换步骤(Python示例)
```python
import json
import os
# 类别映射字典(根据实际数据集修改)
class_map = {"car": 0, "ship": 1, "airplane": 2} # 示例
def json_to_yolo_obb(json_path, output_dir):
with open(json_path) as f:
data = json.load(f)
# 获取图像尺寸
width = data["image_width"] # JSON中图像宽字段名
height = data["image_height"] # JSON中图像高字段名
# 准备输出文件路径
txt_path = os.path.join(output_dir, os.path.splitext(os.path.basename(json_path))[0] + ".txt")
with open(txt_path, 'w') as f_out:
for obj in data["objects"]: # 遍历所有标注对象
# 获取类别ID
class_name = obj["category"]
class_id = class_map.get(class_name, -1) # 未定义类别返回-1
# 获取四边形坐标(假设JSON中为8个值的列表[x1,y1,x2,y2,...])
points = obj["bbox"] # 根据实际JSON字段名调整
# 坐标归一化
normalized_points = [
points[i] / width if i % 2 == 0 else points[i] / height
for i in range(len(points))
]
# 写入TXT行:class_id + 8个归一化坐标
line = f"{class_id} " + " ".join(f"{p:.6f}" for p in normalized_points) + "\n"
f_out.write(line)
# 批量转换
json_dir = "/path/to/json_folder"
output_dir = "/path/to/output_txt"
os.makedirs(output_dir, exist_ok=True)
for json_file in os.listdir(json_dir):
if json_file.endswith(".json"):
json_to_yolo_obb(os.path.join(json_dir, json_file), output_dir)
```
### 三、关键注意事项
1. **坐标顺序**:确保所有四边形的角点顺序(顺时针/逆时针)保持一致
2. **归一化计算**:
$$ (x_{\text{norm}}, y_{\text{norm}}) = \left( \frac{x}{W}, \frac{y}{H} \right) $$
其中 $W$ 为图像宽度,$H$ 为图像高度
3. **JSON字段适配**:
- 根据实际JSON结构调整字段路径(如 `data["objects"]` 可能为 `data["shapes"]`)
- 旋转框表示可能需转换为四边形角点(若JSON中是 `(cx, cy, w, h, angle)` 格式,需先进行坐标转换)
### 四、验证示例
原始JSON片段:
```json
{
"image_width": 1024,
"image_height": 768,
"objects": [
{
"category": "car",
"bbox": [256, 192, 768, 192, 768, 576, 256, 576] // 四边形坐标
}
]
}
```
转换后TXT内容:
```
0 0.250000 0.250000 0.750000 0.250000 0.750000 0.750000 0.250000 0.750000
```
### 五、常见问题处理
- **旋转框转换**:若JSON使用 `(cx, cy, w, h, angle)` 格式,需计算四边形角点:
```python
import math
def rotated_rect_to_points(cx, cy, w, h, angle):
# 角度转弧度
angle_rad = math.radians(angle)
# 计算旋转后的角点
corners = []
for dx, dy in [(-w/2, -h/2), (w/2, -h/2), (w/2, h/2), (-w/2, h/2)]:
x_rot = dx * math.cos(angle_rad) - dy * math.sin(angle_rad) + cx
y_rot = dx * math.sin(angle_rad) + dy * math.cos(angle_rad) + cy
corners.extend([x_rot, y_rot])
return corners
```
- **空文件处理**:当图像无目标时,生成空TXT文件
- **坐标顺序验证**:使用可视化工具检查转换后的四边形是否正确
完成转换后,目录结构应符合Ultralytics要求:
```
dataset/
├── images/
│ ├── train/
│ └── val/
└── labels/
├── train/
└── val/ # 存放转换后的TXT文件
```
阅读全文