【FP60】林业害虫数据集——目标检测、图像分类

发布于:2024-10-17 ⋅ 阅读:(13) ⋅ 点赞:(0)

1、FP60(Forest Pest60)基本介绍

 根据中国江苏省林业有害生物分布情况,筛选出 **15 **个科 60 种,见表,常见且易于获取的林业害虫。主要从互联网上收集林业害虫图像。在收集过程中,以每种林业有害生物的拉丁文学名作为查询关键词,然后在常见的图片搜索引擎中搜索,包括 Google、Bing 和百度。手动筛选搜索结果以过滤低质量图像。我们总共有 7253 张高质量的林业害虫图像,大小从 93×140 到 6016×4106 不等。

 此外,对FP60数据集又进行了二次划分,分别为FP40(40种)、FP15(15种),每个数据集已经预处理为YOLO格式。

 下图显示了该数据集中 60 种林业有害生物的图像,每张图片代表一种林业有害生物。

在这里插入图片描述
在这里插入图片描述

每个数据集的文件结构如下:

   FP60
	|
	|___dataset
	|	  |______images
    |     |        |_______train
    |	  |        |         |__________1-1-1.jpg
    |	  |        |         |__________ ......
    |     |        |_______test
    |	  |        |         |__________2-4-82.jpg
    |	  |        |         |__________ ......
    |     |        |_______val
    |	  |        |         |__________2-4-1.jpg
    |	  |        |         |__________ ......
	|	  |
	|	  |______labels	  
    |     |        |_______train
    |	  |        |         |__________1-1-1.txt
    |	  |        |         |__________ ......
    |     |        |_______test
    |	  |        |         |__________2-4-82.txt
    |	  |        |         |__________ ......
    |     |        |_______val
    |	  |        |         |__________2-4-1.txt
    |	  |        |         |__________ ......

同时,文件中也有对应的处理代码。

2、一些数据处理

 下面这些代码不仅仅只针对当前数据集,对其他目标检测数据集同样适用(注意要求改文件路径)。

2.1、数据划分

import os
import shutil
def get_dataset_name(file):
    file_names = []
    with open(file, 'r', encoding='utf-8') as f:
        lines = f.readlines()
        for line in lines:
            file_names.append(line.split('\n')[0])
    return file_names
def copy_file(s_folder,d_folder,file_name,type='.jpg'):
    s_file = os.path.join(s_folder,file_name+type)
    d_file = os.path.join(d_folder,file_name+type)
    if not os.path.exists(d_file):
        shutil.copyfile(s_file,d_file)
def process_multi_files(file_names,s_folder,d_folder,type):
    for name in file_names:
        copy_file(s_folder,d_folder,name,type)
def main():
    train_file_txt = './labels/train.txt'
    val_file_txt = './labels/trainval.txt'
    test_file_txt_1 = './labels/test.txt'
    test_file_txt_2 = './labels/val.txt'
    
    image_path = './images'
    xml_path = './annotations'

    train_img_fold = './datasets/images/train'
    test_img_fold = './datasets/images/test'
    val_img_fold = './datasets/images/val'

    # train_txt_fold = './datasets/txt/train'
    # test_txt_fold = './datasets/txt/test'
    # val_txt_fold = './datasets/txt/val'

    train_xml_fold = './datasets/xml/train'
    test_xml_fold = './datasets/xml/test'
    val_xml_fold = './datasets/xml/val'
    
    train_file_names = get_dataset_name(train_file_txt)
    val_file_names = get_dataset_name(val_file_txt)
    test_file_names_1 = get_dataset_name(test_file_txt_1)
    test_file_names_2 = get_dataset_name(test_file_txt_2)
    test_file_names_1.extend(test_file_names_2)
    # 复制训练集图片
    process_multi_files(train_file_names,image_path,train_img_fold,'.jpg')
    # 复制验证集图片
    process_multi_files(val_file_names,image_path,val_img_fold,'.jpg')
    #复制测试集图片
    process_multi_files(test_file_names_1,image_path,test_img_fold,'.jpg')
    
    # 复制训练集xml
    process_multi_files(train_file_names,xml_path,train_xml_fold,'.xml')
    # 复制验证集xml
    process_multi_files(val_file_names,xml_path,val_xml_fold,'.xml')
    #复制测试集xml
    process_multi_files(test_file_names_1,xml_path,test_xml_fold,'.xml')
if __name__ == "__main__":
    main()

2.2、xml格式转yolo格式

# 导入相关库
import os
from lxml import etree
from tqdm import tqdm


def voc2txt():
    # 获取xml文件夹下的所有xml文件名,存入列表
    xmls_list = os.listdir(xmls_ori_path)
    for xml_name in tqdm(xmls_list):
        # 打开写入文件
        txt_name = xml_name.replace('xml', 'txt')

        f = open(os.path.join(txts_save_path, txt_name), 'w')  # 代开待写入的txt文件
        with open(os.path.join(xmls_ori_path, xml_name), 'rb') as fp:
            # 开始解析xml文件
            xml = etree.HTML(fp.read())
            width = int(xml.xpath('//size/width/text()')[0])
            height = int(xml.xpath('//size/height/text()')[0])
            # 获取对象标签
            obj = xml.xpath('//object')
            for each in obj:
                name = each.xpath("./name/text()")[0]
                classes = dic[name]
                xmin = int(each.xpath('./bndbox/xmin/text()')[0])
                xmax = int(each.xpath('./bndbox/xmax/text()')[0])
                ymin = int(each.xpath('./bndbox/ymin/text()')[0])
                ymax = int(each.xpath('./bndbox/ymax/text()')[0])
                # 归一化
                dw = 1 / width
                dh = 1 / height
                x_center = (xmin + xmax) / 2
                y_center = (ymax + ymin) / 2
                w = (xmax - xmin)
                h = (ymax - ymin)
                x, y, w, h = x_center * dw, y_center * dh, w * dw, h * dh
                # 写入
                f.write(str(classes) + ' ' + str(x) + ' ' + str(y) + ' ' + str(w) + ' ' + str(h) + ' ' + '\n')
        f.close()  # 关闭txt文件


if __name__ == '__main__':
    dic = {'2-4': "0",  
           '3-1': "1",   
           '5-2': "2",
           '5-3': "3",
           '6-3': "4",
           '6-4': "5",
           '8-3': "6",
           '11-4': "7",
           '12-3': "8",
           '12-4': "9",
           '13-1': "10",
           '14-1': "11",
           '14-3': "12",
           '14-5': "13",
           '15-1': "14",
           }

    #训练数据
    # xmls_ori_path = r"./datasets/xml/train"         # xml文件所在的文件夹
    # txts_save_path = r"./datasets/txt/train"         # txt文件所在的文件夹
    #验证数据
    # xmls_ori_path = r"./datasets/xml/val"         # xml文件所在的文件夹
    # txts_save_path = r"./datasets/txt/val"         # txt文件所在的文件夹
    #测试数据
    xmls_ori_path = r"./datasets/xml/test"         # xml文件所在的文件夹
    txts_save_path = r"./datasets/txt/test"         # txt文件所在的文件夹
    voc2txt()
    

2.3、check标注框

import os
import cv2

class check_label:
    def __init__(self, classes:list, label_path:str, img_path:str, result_path:str=None):
        self.classes = classes
        self.line_width = 5 #线宽
        self.rec_color = (0, 0, 255) #颜色
        self.font_color = (255, 255, 255) #字体颜色
        self.font = cv2.FONT_HERSHEY_SIMPLEX
        self.font_size = 5 #字体大小
        self.font_thickness = 4
        self.font_x_offset = 0#字体x坐标偏移
        self.font_y_offset = -15#字体y坐标偏移

        self.isDrawFontRec = False#是否绘制字体矩形框
        self.isShowFont = False#是否绘制字体
        self.isShowConfidence = False#是否绘制置信度

        self.label_path =  label_path # 数据集标注结果文件(yolo格式)
        self.img_path = img_path  # 图像文件
        self.result_path = result_path  # 在图像上画好标注框文件

        self.label_files = os.listdir(label_path)
        self.img_files = os.listdir(img_path)
        #self.label_files.sort(key=lambda x: int(x[:-4]))
        #self.img_files.sort(key=lambda x: int(x[:-4]))

    def paint(self, imgName, pos):
        img = cv2.imread(self.img_path + "/" + imgName)
        size = img.shape
        imgW = size[1]
        imgH = size[0]
        # print("pos:", len(pos))
        for pos_i in pos:
            # a, x, y, w, h, b = "orange", pos_i[1], pos_i[2], pos_i[3], pos_i[4], pos_i[4]
            pos_i = pos_i.split(' ')
            x_center = float(pos_i[1]) * imgW + 1
            y_center = float(pos_i[2]) * imgH + 1
            x_min = int(x_center - 0.5 * float(pos_i[3]) * imgW)
            y_min = int(y_center - 0.5 * float(pos_i[4]) * imgH)
            x_max = int(x_center + 0.5 * float(pos_i[3]) * imgW)
            y_max = int(y_center + 0.5 * float(pos_i[4]) * imgH)
            x = x_min
            y = y_min
            w = x_max - x_min
            h = y_max - y_min
            # rotate90(imgW, imgH, x_min, y_min, w, h, x_center, y_center)
            # rotate180(imgW, imgH, x_min, y_min, w, h, x_center, y_center)
            # rotate270(imgW, imgH, x_min, y_min, w, h, x_center, y_center)
            # b = float(pos_i[5])
            b = 0.5
            if self.isShowConfidence:
                a = self.classes[int(pos_i[0])]
            else:
                a = ""
            cv2.rectangle(img, (x, y), (x + w, y + h), self.rec_color, self.line_width)
            if self.isDrawFontRec:
                cv2.rectangle(img, (x + self.font_x_offset, y + self.font_y_offset), (x + w, y + abs(self.font_y_offset)), self.rec_color,
                              -1)
            if self.isShowFont:
                cv2.putText(img, '{} {:.3f}'.format(a, b), (x + self.font_x_offset, y + self.font_y_offset), self.font, self.font_size,
                            self.font_color, self.font_thickness)
        cv2.imshow("img", img)
        cv2.waitKey(0)
        # cv2.imwrite(self.result_path + "/" + imgName, img)

    def process(self):
        for label_file, img_file in zip(self.label_files, self.img_files):
            print(img_file, label_file)
            if not os.path.isdir(label_file):
                f = open(self.label_path + "/" + label_file, "r", encoding='utf-8')
                result = f.read().splitlines()
                # print(result)
                self.paint(img_file, result)
                f.close()
classes = ['2-4','3-1','5-2','5-3','6-3','6-4','8-3','11-4',
           '12-3','12-4','13-1','14-1','14-3','14-5','15-1',]
cl = check_label(classes,'./txt','./images')
cl.process()

3、数据集获取

林业害虫数据集下载链接