数据处理:各种数据集格式转换(coco_to_yolo、yolo_to_voc、yolo_to_coco)

1. 内容描述

本文包含了各种数据集格式的转换;数据集中标注文件为.mat格式如何转换;如何通过画目标框的方式确定自己标注文件中每行代表什么(因为数据集格式不同,标签文件中的每行数字代表的目标框也不同);csv文件加载模型失败等。

2. COCO转YOLO

1. 在图片上画出目标框:在某些情况下,拿到的数据集并没有很多细节的描述,所以很多信息都需要我们自己找,但在这个过程中,较为重要的信息之一就是边框的坐标值,换句话说,数据集中得到的四个数值,不知道代表的是什么含义;比如:【左上角x坐标,左上角y坐标,w,h】或者【左上角x坐标,左上角y坐标,右下角x坐标,右下角y坐标】或者【中心点x坐标,中心点y坐标,w,h】。在以下代码中,获取到的mat文件中的框1、框2、框3、框4就存在这种问题。四个数字不确定到底是哪种坐标类型。所以就必须在图片上根据数值用代码画出相对应的目标框,才可以判断。该数据集通过画出目标框,可以确定坐标类型为【左上角x坐标,左上角y坐标,w,h】,为后续生成txt标注文件做准备。

from scipy import io
import cv2
from PIL import Image

# 读取.mat文件的内容(data中包含边界框坐标)
data = io.loadmat('E:/Datasets/dataset/trainval/labels/T0001_XM_20110809100243_02.mat')
# 图片的路径
fname = 'E:/Datasets/dataset/trainval/images/T0001_XM_20110809100243_02.jpg'
# 读取图片
img = cv2.imread(fname, 1)

# 由于该mat文件中有四个边框坐标,所以输出4个边界框信息
for i in range(4):
    print(data['annotation'][0][0][1][i])
# [24 1338  390  280] 边框1
# [3058  369 256 323] 边框2
# [ 590 2072 130 155] 边框3
# [5.10000e-01 1.04851e+03 1.07980e+02 1.38980e+02] # 边框4

pt1 = (24, 1338) # 框1左上角坐标
pt2 = (390+24, 280+1338) # 框1右下角坐标
pt3 = (3058, 369) # 框2左上角坐标
pt4 = (256+3058, 323+369) # 框2右下角坐标
pt5 = (590, 2072)
pt6 = (130+590, 155+2072)
pt7 = (0, 1048)
pt8 = (107+0, 138+1048)
cv2.rectangle(img, pt1, pt2, (0, 0, 255), 2) # 在图片上画出框1
cv2.rectangle(img, pt3, pt4, (0, 0, 255), 2)
cv2.rectangle(img, pt5, pt6, (0, 0, 255), 2)
cv2.rectangle(img, pt7, pt8, (0, 0, 255), 2)
save_path = 'img.jpg' # 画好的图片,保存名称
cv2.imwrite(save_path, img)

2. 描述:文件夹中包含多个mat文件,一个mat文件中包含一个图片上的所有目标框信息。所以生成一个文件夹,其中包含多个txt文件,一个txt文件中包含一个图片上的所有目标框信息。适用于yolo模型。

from scipy import io
from PIL import Image
import os

# 创建一个文件夹(运行一次创建文件夹即可)
os.mkdir('E:/Datasets/dataset/test/labels_txt')

path = "E:/Datasets/dataset/test/labels/" # 文件夹名字
path_img = "E:/Datasets/dataset/test/images/" # 图片文件夹
def mat_to_txt():
    path_files_name = os.listdir(path) # 所有文件的名字
    for i in path_files_name:
        all_file_name = os.path.join(path, i)  # 完整的文件名mat
        data = io.loadmat(all_file_name) # 读到mat文件
        txt_file_name = data['annotation'][0][0][0][0]+'.txt' # txt文件名
        img_file_name = data['annotation'][0][0][0][0]+'.jpg' # 图片名
        path_txt = 'E:/Datasets/maize_counting_dataset/test/labels_txt/' # 前缀
        path_file_txt = os.path.join(path_txt, txt_file_name) # 完整的文件名txt
        path_img_file = os.path.join(path_img, img_file_name) # 完整的图片名jpg
        img2 = Image.open(path_img_file) # 读到图片
        w1 = img2.width # 图片的宽
        h1 = img2.height # 图片的高
        file = open(path_file_txt, 'a') # 创建文件名正常的文件
        label_counts = int(data['annotation'][0][0][1].size / 4)  # 所有锚框数
        for i in range(label_counts):
            label_lines = [] # 一个目标框
            x, y, w, h = data['annotation'][0][0][1][i] # 获取mat文件中的坐标信息
            x2 = round((x + w + x) / (2.0 * w1), 6) # 进行归一化,因为yolo模型使用的是归一化的坐标
            y2 = round((y + h + y) / (2.0 * h1), 6) 
            w2 = round((w) / (1.0 * w1), 6)
            h2 = round((h) / (1.0 * h1), 6)
            label_lines.append(0)
            label_lines.append(x2)
            label_lines.append(y2)
            label_lines.append(w2)
            label_lines.append(h2)
            s = str(label_lines)[1:-1].split(',') # 截取'['与']'
            a = ''
            for i in s:
                a = a + str(i) + ''
            file.write(a + '\n') # 写入文件
if __name__ == '__main__':
    mat_to_txt()

  3. 数据集中确定四个值的具体含义。YOLO模型使用的是归一化后的(中心点x,中心点y,w,h)。

import cv2

# 图片和标签路径
image_path = "E:/project/yh/due_data/check_box/Hebei2010_00140_Gc.jpg"
label_path = "E:/project/yh/due_data/check_box/Hebei2010_00140_Gc.txt"

# 读取图片
img = cv2.imread(image_path, 1) # 在图片上标记box
height, width, channels = img.shape # 得到图片宽高

# 读标签文件中的标注框
c = open(label_path) # 打开文件
data = c.readline() # 读取第一行
x, y, w, h = data.split(" ")[1:] # 读取的数值为字符串类型
x = float(x)
y = float(y)
w = float(w)
h = float(h)

# 将归一化的x,y,w,h转换为原来的值,在图片上画box
w1 = w * width
h1 = h * height
# x,y有两种情况:1.x,y是box左上角点坐标,2.x,y是box中心点坐标
# 1. 左上角点
(x + w + x) / (2.0 * w1)
x1 = (x * (2.0 * width) - w1) / 2
y1 = (y * (2.0 * height) - h1) / 2
print(x1, y1, w1, h1) # 2757.001536 1.0013760000000218 162.000384 277.999488

pt1 = (2757, 1) # 左上角的点
pt2 = (2757 + 162, 1 + 278) # 右下角的点
# 画图
# pt1 = (x1, y1)
# pt2 = (x1 + w1, y1 + h1)
cv2.rectangle(img, pt1, pt2, (0, 0, 255), 2) # 在图片上画出框1
save_path = 'img.jpg' # 画好的图片,保存名称
cv2.imwrite(save_path, img)

图1. 已画框的图片(标注框在图片右上部分)

4. 数据集处理:在训练模型过程中,可能会需要对数据集图片尺寸进行改变,例如将原图片大小改变为(1240, 960),在这种情况下,将数据集格式转换为YOLO格式(标签,归一化后的中心点X,归一化后的中心点Y,归一化后的W,归一化后的H)是不需要改变的,也就是标签文件夹中所有内容不需要改变(因为都是归一化后的,就相当于已经进行了单位化),所以只需要给图片进行尺寸转换即可。

import os
from PIL import Image
image_folder = "" # 原始图片文件路径
os.mkdir("") # 创建新的图片文件夹,存放改变大小后的图片

def resize_img(folder):
    all_images = os.listdir(folder) # 图片名
    for i in all_images:
        image_name = os.path.join(folder, i)  # 图片全名
        img = Image.open(image_name)
        img_size = img.resize((1024, 960), Image.ANTIALIAS) # 改变的尺寸大小
        path_img = "" # 创建的新的文件夹名
        path_image_name = os.path.join(path_img, i) # 修改后的图片路径
        img_size.save(path_image_name) # 保存

if __name__ == '__main__':
    resize_img(image_folder)

5. 数据集处理:在某些数据集中,会存在不同格式的情况,所以需要进行重新划分(划分为适合YOLO的格式),以下代码就是将数据集划分为train、test、val(适合YOLO格式)

import os
import shutil
# 一直替换两个文件(因为日期)
path_imgs_folder = "F:/datasets/MT/images"
path_labels_foler = "F:/datasets/MT/labels"

img_train_folder = "F:/datasets/MT_dataset/images/train"
img_test_folder = "F:/datasets/MT_dataset/images/test"
img_val_folder = "F:/datasets/MT_dataset/images/val"

label_train_folder = "F:/datasets/MT_dataset/labels/train"
label_test_folder = "F:/datasets/MT_dataset/labels/test"
label_val_folder = "F:/datasets/MT_dataset/labels/val"

def due_files():
    path_labels = os.listdir(path_labels_foler) # 读取标签文件夹
    for i in path_labels:
        if i.split('_')[-1] == 'valid.txt':
            path_img = i.split('.')[0] + ".jpg"
            path_im = os.path.join(path_imgs_folder, path_img)  # 图片完整路径
            path_la = os.path.join(path_labels_foler, i)  # 标签完整路径
            shutil.copy(path_im, img_val_folder)  # 图片 原文件,新文件夹
            shutil.copy(path_la, label_val_folder)  # 标签 原文件,新文件夹
        elif i.split('_')[-1] == 'test.txt':
            path_img = i.split('.')[0] + ".jpg"
            path_im = os.path.join(path_imgs_folder, path_img)  # 图片完整路径
            path_la = os.path.join(path_labels_foler, i)  # 标签完整路径
            shutil.copy(path_im, img_test_folder)  # 图片 原文件,新文件夹
            shutil.copy(path_la, label_test_folder)  # 标签 原文件,新文件夹
        else: # train
            path_img = i.split('.')[0] + ".jpg"
            path_im = os.path.join(path_imgs_folder, path_img)  # 图片完整路径
            path_la = os.path.join(path_labels_foler, i)  # 标签完整路径
            shutil.copy(path_im, img_train_folder)  # 图片 原文件,新文件夹
            shutil.copy(path_la, label_train_folder)  # 标签 原文件,新文件夹
if __name__ == '__main__':
    due_files() 

3. YOLO转VOC

1. coco转voc:(左上点x, 左上点y, w , h)  -----> (左上点x, 左上点y, 右下点x, 右下点y)。原图像的大小,原图像的(左上角x, 左上角y, w, h),我们将原图像进行尺寸调整到现有尺寸,原图像的(左上角x, 左上角y, w, h)也会发生变化。

2. yolo转voc:(批量归一化中心点x, 批量归一化中心点y, w, h)---> (左上角x, 左上角y, 右下角x, 右下角y)。先使用一张图片进行测试,将yolo格式的labels转为voc格式,再使用cv2将标注框画出来,发现计算过程正确。结果如图2所示:

import os
import cv2
# 先用一张图片进行测试(原图像大小1024*960)
if __name__ == '__main__':
    # yolo_to_voc(path)
    img = cv2.imread("img.png", 1) # 读取一张图片进行测试
    data = open("test.txt").readlines() # 标签文件名
    for j in data:
        x = float(j.split(" ")[1])
        y = float(j.split(" ")[2])
        w = float(j.split(" ")[3])
        h = float(j.split(" ")[4])
        w1 = w * 1024
        h1 = h * 960
        x1 = x * 1024 - w1 / 2
        y1 = y * 960 - h1 / 2
        x2 = x1 + w1
        y2 = y1 + h1
        pt1 = (int(x1), int(y1))
        pt2 = (int(x2), int(y2))
        cv2.rectangle(img, pt1, pt2, (0, 0, 255), 3)
    save_path = "img_new.png"
    cv2.imwrite(save_path, img)

图2

 3. yolo转voc:批量转换步骤

        3.1 第一步:参照原始VOC2007数据集的文件层次创建四个文件夹,首先创建一个VOCdevkit文件夹,VOCdevkit文件夹中再创建Annotations、JPEGImages、ImageSets三个文件夹,最后在ImageSets文件夹下再创建一个Main文件夹。

        3.2 第二步:将数据集所有图片(包括test、train、val中所有图片)都放到JPEGImages文件夹下。

        3.3 第三步: 在Main文件夹下创建四个文件,train.txt、test.txt、val.txt、trainval.txt。

        3.4 第四步:将训练集、测试集、验证集、训练验证集图片名称,分别放入train.txt、test.txt、val.txt、trainval.txt文件中(注意:没有后缀),如图3所示,每行代表一个图片名(目前查看的资料上都会对文件名进行修改,修改为01、02、03这种名称,但是本人在实验过程中并没有按照资料进行名称修改,主要是麻烦,所以初步尝试使用自己原有数据集图片名称,图3是简单示例图),添加名称的代码如下:

图3  

import os
# yolo格式标签文件夹路径
init_test_path = ""
init_train_path = ""
init_val_path = ""

# VOC格式中Main下的test.txt等文件,每次运行,一一对应即可
new_test_path = ""
new_train_path = ""
new_val_path = ""
def due_txt():
    all_val_name = os.listdir(init_val_path) # open folder
    file = open(new_val_path, 'a') # open file
    for i in all_val_name:
        file.write(i.split('.')[0] + '\n') # write image_name

if __name__ == '__main__':
    due_txt()

        3.5 第五步:处理Annotations文件夹,如图4所示,为Annotations文件夹中数据格式。代码如下。

图4 VOC数据格式中,Annotations文件夹中每个xml文件的格式 

import os
from lxml.etree import Element, tostring
from lxml.etree import SubElement as subElement

# 所有图片所在文件夹data/VOC/JPEGImages
init_all_image_path = "data/VOC/JPEGImages"

# Annotations文件夹
annotation_folder = "data/VOC/Annotations"

# YOLO格式中test、train、val标签所在位置
#init_all_test_labels = "dataset/labels/test"
# init_all_train_labels = "dataset/labels/train"
init_all_val_labels = "dataset/labels/val"

def due_annotation():
    # all_label_name = os.listdir(init_all_train_labels)
    # all_label_name = os.listdir(init_all_test_labels)
    all_label_name = os.listdir(init_all_val_labels)  # 改变val、train、test
    for i in all_label_name:
        file_name = i.split(".")[0] # image_name 前缀
        file_name_xml = file_name + ".xml"
        file_image_name = file_name + '.jpg'
        file_name_xml_path = os.path.join(annotation_folder, file_name_xml)
        file_name_image_path = os.path.join(init_all_image_path, file_image_name)
        # file_name_label_path = os.path.join(init_all_train_labels, i)
        # file_name_label_path = os.path.join(init_all_test_labels, i)
        file_name_label_path = os.path.join(init_all_val_labels, i)  # 改变val、train、test
        if os.path.exists(file_name_xml_path):
            break
        else:
            node_root = Element('annotation') # root
            node_folder = subElement(node_root, 'folder') # 子节点
            node_folder.text = "VOC"

            node_filename = subElement(node_root, 'filename') #子节点
            node_filename.text = file_image_name

            node_path = subElement(node_root, 'path') # 子节点
            node_path.text = file_name_image_path

            node_source = subElement(node_root, 'source') #子节点
            node_source.text = "Unknown"

            node_size = subElement(node_root, 'size') #子节点
            node_size_width = subElement(node_size, 'width') # 子子节点
            node_size_width.text = '%s' % int("1024")
            node_size_height = subElement(node_size, 'height')
            node_size_height.text = '%s' % int("960")
            node_size_depth = subElement(node_size, 'depth')
            node_size_depth.text = '%s' % int("3")

            node_segmented = subElement(node_root, 'segmented')
            node_segmented.text = '%s' % int("0")
            
            # 将YOLO格式标签转换为VOC格式,如上文中3.YOLO转VOC代码所示
            with open(file_name_label_path, 'r') as f: # 打开标签文件
                for line in f.readlines(): # 读每行数据
                    node_object = subElement(node_root, 'object') #子节点
                    node_object_name = subElement(node_object, 'name')
                    node_object_name.text = 'maize'
                    node_object_pose = subElement(node_object, 'pose')
                    node_object_pose.text = 'Frontal'
                    node_object_truncated = subElement(node_object, 'truncated')
                    node_object_truncated.text = '%s' % int("0")
                    node_object_diffcult = subElement(node_object, 'diffcult')
                    node_object_diffcult.text = '%s' % int("0")
                    node_object_bndbox = subElement(node_object, 'bndbox')
                    x = float(line.split(" ")[1])
                    y = float(line.split(" ")[2])
                    w = float(line.split(" ")[3])
                    h = float(line.split(" ")[4])
                    w1 = w * 1024
                    h1 = h * 960
                    x1 = x * 1024 - w1 / 2
                    y1 = y * 960 - h1 / 2
                    x2 = x1 + w1
                    y2 = y1 + h1
                    node_object_bndbox_xmin = subElement(node_object_bndbox, 'xmin')
                    node_object_bndbox_xmin.text = '%s' % int(x1)
                    node_object_bndbox_ymin = subElement(node_object_bndbox, 'ymin')
                    node_object_bndbox_ymin.text = '%s' % int(y1)
                    node_object_bndbox_xmax = subElement(node_object_bndbox, 'xmax')
                    node_object_bndbox_xmax.text = '%s' % int(x2)
                    node_object_bndbox_ymax = subElement(node_object_bndbox, 'ymax')
                    node_object_bndbox_ymax.text = '%s' % int(y2)

            xml = tostring(node_root, pretty_print=True) # 将上面设定的一串节点信息导出
            with open(file_name_xml_path, 'wb') as f1: # 将节点信息写入到文件路径中
                f1.write(xml)

if __name__ == '__main__':
    due_annotation()

 4. 训练pytorch-ssd模型

        主要参考:http://t.csdnimg.cn/XHSkG

        权重参考:http://t.csdnimg.cn/hklvD

4. YOLO转COCO

1. 在制作annotations.json文件时,可能会出现该错误:PermissionError: [Errno 13] Permission denied: ‘XXXX ,

解决:该错误一般情况下,并不是因为文件权限不够造成,一般都是因为前面代码中生成的文件路径有问题,检查自己的文件路径是否正确。

2. yolo数据转coco

链接:目标检测标注文件yolov5(txt)格式转coco(json)格式详解及代码实现_txt转coco-CSDN博客

链接: YOLO格式标签转换为COCO格式-CSDN博客

5. 使用CSV标签文件,加载模型失败

 csv标注文件在使用时,很容易忽略一个重要问题,csv文件中有可能保留着列名。在读取时就会出现错误,提示无法找到对应的图片文件。因此需要对读取的内容进行简单处理。在DataLoader中使用下列代码进行读取,原始代码在读取时,会将列名作为数据加入列表中,所以只需要在列表中去除列表中index为0的内容即可。

class DatasetLoader(Dataset):
    def __init__(self, csv_path):
        self.csv_file = csv_path
        with open(self.csv_file, 'r') as file:
            # self.data = list(csv_read(file)) 原始代码
            self.data = list(csv.reader(file))[1:]

6. 在yolov8预测图中标记目标总数(适用于单目标)

步骤一:运行predict,将结果栏中结果复制,放在yolov8.txt文件中(代码中file_yolov8所指的路径) 

步骤二:创建images文件夹,将predict后的带有标签以及置信度的图片,全部复制到该文件中(代码中folder_images所指的路径)

步骤三:创建images_total文件夹(代码中folder_images_total所指的路径)

运行即可

import cv2
import os

folder_images = "E:/project/yh/Utils_Data/draw_image_total/images/" # 预测后的图片文件夹(图片上包含标签与置信度)
folder_images_total = "E:/project/yh/Utils_Data/draw_image_total/images_total/" # 标记总数图片的文件夹
# 根据模型预测的数量:在每张图片上的标记玉米雄穗总数量
def yolov8_total():
    file_yolov8 = "E:/project/yh/Utils_Data/draw_image_total/yolov8.txt"
    c = open(file_yolov8) # 打开文件
    yolov8_data = c.readlines()
    yolov8_image = []  # 图片路径
    yolov8_number = []  # 标签总数
    for i in yolov8_data:
        images_total = i.split(" ")[-3]
        if not images_total.isdigit():
            images_total = "0"
        image = i.split("\\")[-1].split(":")[0]
        images_path = os.path.join(folder_images, image) # 预测后图片的绝对路径
        img = cv2.imread(images_path, 1)
        cv2.putText(img, images_total, (100, 200), cv2.FONT_HERSHEY_SIMPLEX, 5, (255, 0, 0), 5)
        images_total_path = os.path.join(folder_images_total, image)
        cv2.imwrite(images_total_path, img)

if __name__ == '__main__':
    yolov8_total()

 结果展示图

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
voc数据集coco数据集的标注格式与yolo数据集的标注格式是有一些差异的。 VOC数据集的标注格式是使用XML文件来描述每个物体的位置和类别。每个XML文件对应一张图像,其中包含了图像的宽度、高度和深度信息,以及物体的类别、边界框的坐标等。VOC数据集的标注格式相对简单,适合较小规模的目标检测任务。 COCO数据集的标注格式是使用JSON文件来描述每个物体的位置和类别。每个JSON文件对应一张图像,其中包含了图像的信息、物体的类别和边界框的坐标等。COCO数据集的标注格式相对复杂,支持多种类型的目标检测任务,并且可以标注更详细的物体属性。 而YOLO数据集的标注格式是使用TXT文件来描述每个物体的位置和类别。每个TXT文件对应一张图像,其中每一行表示一个物体的类别和边界框的坐标。YOLO数据集的标注格式相对简洁,适合实时目标检测任务。 因此,对于不同的目标检测算法,需要根据其要求选择相应的数据集标注格式,以保证算法的正常运行。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [objectDetectionDatasets目标检测数据集制作:VOC,COCO,YOLO等常用数据集格式的制作和互相换](https://download.csdn.net/download/weixin_42614745/85278721)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [YOLO目标检测+扑克牌数据集已标注可以直接使用(3400张图像+对应已标注VOCCOCOYOLO格式文件).rar](https://download.csdn.net/download/m0_62143653/87774669)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [目标检测数据集VOC格式工程车辆数据集系列17渣土车数据集-3449张](https://download.csdn.net/download/FL1623863129/87454796)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值