关于YOLOV5数据处理时的小问题!
1.写这个帖子的初衷
最近在做基于yolov5目标检测的行人跌倒检测,在网上找了很久的图片数据(也有人家标注好的label,自己标注太慢了,好像下载的是百度飞桨)。不过拿到后放到模型中试了试,直接报错ZeroDivisionError…因为我是直接下载的人家的XML数据文件,转化之后就去调用了,没检查数据的有效性。根据报错发现有的图片文件属性不存在。
就是圆圈的地方width或者height值为0,这样的XML文件在转换成txt文件时一定会报错。
2.想到的解决办法
对于这样的情况肯定是要把无效的XML文件拿出来然后重新用labelimg进行标注。下载的跌倒图片一共1442张,无效的图片不多,只需要将其重新标注再把原来的XML文件覆盖掉,最后所有图片转换txt。
3.期间遇到的小问题
——由于我是想写个程序,通过XML文件的size属性检测出无效的文件,然后按顺序打印出无效的图片名称,再将无效的图片文件复制到另一个文件夹方便标注。所以,就会出现一些不知道的东西…
—》涉及到文件操作,os.listdir(path)这句代码返回的是文件夹下所有文件的名称,但是是无序的。
—》检测到无效的XML文件,需要将其对应的jpg文件复制到别的文件夹下。
这些数据文件命名一般都有规律。跌倒检测的标签比较简单,只涉及一个类别**‘fall’**。
4.处理阶段
----对于从网上找的一大堆图片文件,命名五花八门,而且还有的是webp类型文件,对于模型的数据文件,需要一定的处理(这里说的不是预处理)。
----对于非JPG,PNG的图片首先得格式转换—在线免费格式转换
----文件重命名代码很简单:
对于检索无效文件的思路,大致上是:
1.得到源XML文件夹下的所有的文件
2.依靠程序提取出文件名中的数字(因为模型数据大都是以数字+英文特征命名,比如**‘’fall_100.xml‘’**)
3.构建键值对,并对其排序(还涉及到元组、列表等)
4.利用copy函数复制无效文件
代码实现:
# coding=utf-8
import xml.dom.minidom
import re
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir , getcwd
from os.path import join
import glob
import shutil
'''
注意:尽量将需要检查的XML文件夹(如Annotations)、原始jpg文件夹(如images)、数据无效的XML文件所对应的新jpg文件夹(need_to_recharge)、
无效XML文件重新标注的labels文件夹尽量在同一目录下。
'''
path='E:/Yolov/yolov5_fall_detect/fall-detection-data-set-master/Annotations/' #需要修改mxl文件夹的地址
files=os.listdir(path) #得到文件夹下所有文件名称
key=[] #用来存放文件名中的数字,作为键
value=[]# 用来存放文件名,作为值
# 下面的程序主要是 按顺序 筛选出XML数据文件中 width 等无效文件
def read_width(file_name):
in_file = open(path+file_name) #xml文件路径
f = open(path+file_name)
xml_text = f.read()
root = ET.fromstring(xml_text)
f.close()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
return w,h
for xmlFile in files: #遍历文件夹
pattern=r'\d+' #正则表达式-提取字符串中的数字的规则
match_obj=re.findall(pattern,xmlFile) #得到数字
key.append(int(match_obj[0]))
value.append(xmlFile)
file_right_order=dict(zip(key,value)) #构建键值对
file_name_tuplelist=list(zip(file_right_order.keys(),file_right_order.values()))#转为元组,方便排序
file_name_sortedlist=sorted(file_name_tuplelist,reverse=False)#升序排列,是一个以元组为元素的列表
file_name=[] #按顺序排列的 XML文件名 列表
for index_tuple in file_name_sortedlist:
file_name.append(index_tuple[1])
#下面将width或者height为0的 图片文件 复制到新的文件夹下,方便打loyov5标签
dstpath='E:/Yolov/yolov5_fall_detect/fall-detection-data-set-master/need_to_recharge'
picture_path='E:/Yolov/yolov5_fall_detect/fall-detection-data-set-master/images/' #跟XML文件夹同目录的图片文件夹
for XML_file in file_name:
XML_filepath=os.path.join(path,XML_file)
if not os.path.isdir(XML_filepath):
w,h=read_width(XML_file)
if w==0 or h==0:
srcfile=picture_path+XML_file.split('.')[0]+'.jpg' #无效的XML文件对应的image文件
if not os.path.exists(dstpath):
os.makedirs(dstpath)
shutil.copy(srcfile, dstpath)
print(XML_file+'文件无效,对应的JPG文件已复制到'+dstpath+'文件夹下。')
检测成功并打印好的截图如下:
代码里面注释有点乱,这里我的文件层级如下(供参考,主要是方便看):
5.附加一下格式转换代码
格式转换的代码我没看,直接拿来用了,算是搬个砖…YOLOV5的数据文件在打标签时,也是可以直接保存成txt。
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir , getcwd
from os.path import join
import glob
classes = ["fall"]
def convert(size, box):
dw = 1.0/size[0]
dh = 1.0/size[1]
x = (box[0]+box[1])/2.0
y = (box[2]+box[3])/2.0
w = box[1] - box[0]
h = box[3] - box[2]
x = x*dw
w = w*dw
y = y*dh
h = h*dh
return (x,y,w,h)
def convert_annotation(image_name):
in_file = open('E:/Yolov/yolov5_fall_detect/fall-detection-data-set-master/Annotations/'+image_name[:-3]+'xml') #xml文件路径
out_file = open('E:/Yolov/yolov5_fall_detect/fall-detection-data-set-master/XMLtoTxt/'+image_name[:-3]+'txt', 'w') #转换后的txt文件存放路径
f = open('E:/Yolov/yolov5_fall_detect/fall-detection-data-set-master/Annotations/'+image_name[:-3]+'xml')
xml_text = f.read()
root = ET.fromstring(xml_text)
f.close()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
cls = obj.find('name').text
if cls not in classes:
print(cls)
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
bb = convert((w,h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
wd = getcwd()
if __name__ == '__main__':
for image_path in glob.glob("E:/Yolov/yolov5_fall_detect/fall-detection-data-set-master/images/*.jpg"): #每一张图片都对应一个xml文件这里写xml对应的图片的路径
image_name = image_path.split('\\')[-1]
convert_annotation(image_name)
6.结尾
在贴几张涉及到python基础知识的图片,以防后面又挖了,做个记录。
----->字典(键值对)
----->元组
----->re匹配数字
以上是我在学习的时候,遇到的问题,并加以总结!。。。就是怕自己忘的快,到时候遇到又得搜好一会儿。
Over!2024.03.14