小目标检测工具:matlab标注文件转换为txt文件,并在图片上画出目标框,对数据集进行划分

1. 问题描述

1. 问题描述:由于本人在实验过程中,需要使用一个标注文件为.mat的数据集,但是由于没有matlab软件,所以无法打开,为此写了该脚本,将matlab标注文件转换为txt文件。

2. 代码

2. 在图片上画出目标框:在某些情况下,拿到的数据集并没有很多细节的描述,所以很多信息都需要我们自己找,但在这个过程中,较为重要的信息之一就是边框的坐标值,换句话说,数据集中得到的四个数值,不知道代表的是什么含义;比如:【左上角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)

3. 描述:文件夹中包含多个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()

4. 数据集中确定四个值的具体含义。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. 已画框的图片(标注框在图片右上部分)

5. 数据集处理:在训练模型过程中,可能会需要对数据集图片尺寸进行改变,例如将原图片大小改变为(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)

6. 数据集处理:在某些数据集中,会存在不同格式的情况,所以需要进行重新划分(划分为适合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() 

猜你喜欢

转载自blog.csdn.net/weixin_44813538/article/details/132266283