python:将 NWPU_VHR-10 遥感目标检测数据集转换成 YOLO 格式

作者:CSDN @ _养乐多_

本文将介绍将 NWPU_VHR-10 遥感目标检测数据集转换成 YOLO 格式的 python 脚本。

在这里插入图片描述



一、数据集介绍

1.1 数据集下载

https://opendatalab.com/OpenDataLab/NWPU_VHR-10

1.2 数据集介绍

NWPU VHR-10数据集是具有挑战性的十类地理空间对象检测数据集。该数据集总共包含800幅VHR光学遥感图像,其中715幅彩色图像是从谷歌地球获得的,空间分辨率从0.5到2米,从具有0.08 m的空间分辨率的Vaihingen数据获取85幅经锐化的彩色红外图像。

该数据集被分成两组:

  • a) 在图像中包含至少一个目标的正图像集包含650图像。
  • b) 负图像集包含150图像,并且它不包含任何目标。
    由此,正面图像集、757飞机、302船只、655储罐、390棒球钻石、524网球场、159篮球场、163地面田径场、224港口、124桥梁和477车辆被手动注释为边界框和用于地面真相的实例面具。

使用全部数据集或者部分数据的时候,需要引用以下论文:

  • Gong Cheng, Junwei Han, Peicheng Zhou, Lei Guo. Multi-class geospatial object detection and geographic image classification based on collection of part detectors. ISPRS Journal of Photogrammetry and Remote Sensing, 98: 119-132, 2014.
  • Gong Cheng, Junwei Han. A survey on object detection in optical remote sensing images. ISPRS Journal of Photogrammetry and Remote Sensing, 117: 11-28, 2016.
  • Gong Cheng, Peicheng Zhou, Junwei Han. Learning rotation-invariant convolutional neural networks for object detection in VHR optical remote sensing images. IEEE Transactions on Geoscience and Remote Sensing, 54(12): 7405-7415, 2016.
1.3 数据格式

文本文件的每一行定义了一个对象边界框,格式如下:

(x1,y1),(x2,y2),a

其中(x1,y1)表示边界框的左上角坐标,(x2,y2)表示边界框的右下角坐标,而a是对象类别(1-飞机,2-船只,3-储罐,4-棒球场,5-网球场,6-篮球场,7-田径场地,8-港口,9-桥梁,10-车辆)。

二、格式转换

我们需要将 NWPU_VHR-10 数据集转换成 YOLO 格式,以进行目标检测。

就是将

(x1,y1),(x2,y2),a

转换为 YOLO 格式

class, x_center, y_center, width, height

YOLO的标注格式非常简洁,对于每张图片中的每一个对象,都会有一行描述该对象的信息。每一行包含五个值:

  • 类别的索引(class index):表示该对象所属的类别,在类别列表中的索引(从0开始)。
  • 中心点的 x 坐标(x_center):对象边界框中心点相对于图像宽度的归一化位置(范围 0 到 1)。
  • 中心点的 y 坐标(y_center):对象边界框中心点相对于图像高度的归一化位置(范围 0 到 1)。
  • 边界框的宽度(width):边界框的宽度相对于图像宽度的归一化值(范围 0 到 1)。
  • 边界框的高度(height):边界框的高度相对于图像高度的归一化值(范围 0 到 1)。

举个例子,如果有一个图像,其中包含一个类别为“人”的对象,该对象的边界框中心点位于图像的 (0.4, 0.5),边界框的宽度为图像宽度的 0.1 倍,高度为图像高度的 0.2 倍,并且“人”这个类别的索引是 0,则对应的 YOLO 格式的标注文件中的一行可能如下所示:

0 0.4 0.5 0.1 0.2

三、完整代码

使用时,需要修改 class_map 和文件路径。

import os
from PIL import Image


def convert_to_yolo_format(x1, y1, x2, y2, img_width, img_height):
    dw = 1.0 / img_width
    dh = 1.0 / img_height
    center_x = (x1 + x2) / 2.0
    center_y = (y1 + y2) / 2.0
    width = x2 - x1
    height = y2 - y1
    center_x *= dw
    center_y *= dh
    width *= dw
    height *= dh
    return (center_x, center_y, width, height)


# 文件路径
annotations_dir = r'E:\DataSet\NWPUVHR10dataset\annotations'
images_dir = r'E:\DataSet\NWPUVHR10dataset\positive'
output_dir = r'E:\DataSet\NWPUVHR10dataset\yolo_labels'

# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)

# 类别映射
class_map = {
    '1': '0',  # 飞机
    '2': '1',  # 船只
    '3': '2',  # 储油罐
    '4': '3',  # 棒球场
    '5': '4',  # 网球场
    '6': '5',  # 篮球场
    '7': '6',  # 跑道场地
    '8': '7',  # 港口
    '9': '8',  # 桥梁
    '10': '9'  # 车辆
}


def get_image_size(image_path):
    # 打开图片文件
    with Image.open(image_path) as img:
        # 获取图片的宽度和高度
        width, height = img.size
        return width, height


def parse_bbox(line):
    try:
        # 去掉括号,并用逗号分割
        line = line.strip().replace('(', '').replace(')', '')
        parts = line.split(',')
        if len(parts) != 5:
            raise ValueError("Invalid line format")

        x1, y1, x2, y2 = map(int, parts[:4])
        class_id = parts[4]

        return x1, y1, x2, y2, class_id
    except ValueError as e:
        print(f"Error parsing line: {line}. Error: {e}")
        return None, None, None, None, None


# 遍历标注文件
for annotation_file in os.listdir(annotations_dir):
    if annotation_file.endswith('.txt'):
        # 读取对应的图像文件
        image_file = os.path.splitext(annotation_file)[0] + '.jpg'
        image_path = os.path.join(images_dir, image_file)

        # 打印调试信息
        print(f"Processing image: {image_path}")

        if not os.path.exists(image_path):
            print(f"Image file {image_path} does not exist, skipping.")
            continue

        # 获取图像大小
        img_width, img_height = get_image_size(image_path)

        # 读取标注文件
        annotation_path = os.path.join(annotations_dir, annotation_file)
        with open(annotation_path, 'r') as f:
            lines = f.readlines()

        # 输出文件路径
        output_path = os.path.join(output_dir, annotation_file)

        with open(output_path, 'w') as out_f:
            for line in lines:
                # 解析标注信息
                x1, y1, x2, y2, class_id = parse_bbox(line)
                if x1 is None:
                    continue

                class_id = class_map.get(class_id, None)
                if class_id is None:
                    print(f"Unknown class id: {class_id}")
                    continue

                # 转换为YOLO格式
                center_x, center_y, width, height = convert_to_yolo_format(x1, y1, x2, y2, img_width, img_height)

                # 写入输出文件
                out_f.write(f"{class_id} {center_x} {center_y} {width} {height}\n")

print("Conversion completed.")

四、划分数据集

如果需要将图片和标签数据集按比例划分为训练、验证、测试数据集,请参考以下博客。

参考博客《YOLO:VOC格式数据集转换为YOLO数据集格式》中的第2节。

  • 29
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_养乐多_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值