文章目录
1、FP60(Forest Pest60)基本介绍
根据中国江苏省林业有害生物分布情况,筛选出 **15 **个科 60 种,见表,常见且易于获取的林业害虫。主要从互联网上收集林业害虫图像。在收集过程中,以每种林业有害生物的拉丁文学名作为查询关键词,然后在常见的图片搜索引擎中搜索,包括 Google、Bing 和百度。手动筛选搜索结果以过滤低质量图像。我们总共有 7253 张高质量的林业害虫图像,大小从 93×140 到 6016×4106 不等。
此外,对FP60数据集又进行了二次划分,分别为FP40(40种)、FP15(15种),每个数据集已经预处理为YOLO格式。
下图显示了该数据集中 60 种林业有害生物的图像,每张图片代表一种林业有害生物。
每个数据集的文件结构如下:
python
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、数据划分
python
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格式
python
# 导入相关库
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标注框
python
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()