数据处理功能脚本
我们在处理数据集时基本流程基本是:
1、确定标签
2、数据整理
3、数据集拆分
4、格式转换
以下将主要通过这四步记录模型训练之前数据处理的相关工作
1、确定标签
1.1、 查看xml文件夹的标签种类
做数据集做操作(已标注过)首先要知道数据集的种类有那些,故需要统计Annotations文件夹下xml标签数量种类
import os
from unicodedata import name
import xml.etree.ElementTree as ET
import glob
def count_num(indir):
'''
统计Annotations文件夹下xml标签数量种类
:param indir: 文件夹路径
:return:
'''
label_list = []
# 提取xml文件列表
os.chdir(indir)
annotations = os.listdir('.')
print(annotations)
print(len(annotations))
# annotations = glob.glob(str(annotations) + '*.xml')
print(annotations)
print(len(annotations))
dict = {} # 新建字典,用于存放各类标签名及其对应的数目
for i, file in enumerate(annotations): # 遍历xml文件
# actual parsing
in_file = open(file, encoding='utf-8')
tree = ET.parse(in_file)
root = tree.getroot()
# 遍历文件的所有标签
for obj in root.iter('object'):
name = obj.find('name').text
if (name in dict.keys()):
dict[name] += 1 # 如果标签不是第一次出现,则+1
else:
dict[name] = 1 # 如果标签是第一次出现,则将该标签名对应的value初始化为1
# 打印结果
print("各类标签的数量分别为:")
for key in dict.keys():
print(key + ': ' + str(dict[key]))
label_list.append(key)
print("标签类别如下:")
print(label_list)
if __name__ == '__main__':
# xml文件所在的目录,修改此处
indir = r'E:\训练结果\变电站17种缺陷检测\train\annots'
count_num(indir) # 调用函数统计各类标签数目
运行后可以得出该annotations下存在的标签种类及统计数量
1.2、 批量修改标签属性
如果遇到数据集中的标签不符合想要的类型,需要全部修改,则需要批量对xml做调整
#!/usr/bin/env python3.8
# -*- coding: utf-8 -*-
import glob
import xml.etree.ElementTree as ET
def replace_label(old, new, path):
"""
function: 使用python xml解析树解析xml文件,批量修改xml文件里object节点下name节点的text
date:2023/10/26
"""
# path = r'D:/pythonProject/1028_fall/fire1026/Annotations' # xml文件夹路径
i = 0
for xml_file in glob.glob(path + '/*.xml'):
# print(xml_file)
tree = ET.parse(xml_file)
obj_list = tree.getroot().findall('object')
for per_obj in obj_list:
if per_obj[0].text == f'{old}': # 人物标签“person”
per_obj[0].text = f'{new}' # 修改成“coverallno”
i = i+1
tree.write(xml_file) # 将改好的文件重新写入,会覆盖原文件
print('共完成了{}处替换'.format(i))
if __name__ == '__main__':
replace_label('smoking', 'xy', r'D:\数据集\通用\吸烟\smokeing\smokeing\test\Annotations')
2、数据整理
2.1、 挑选指定的标签种类
如果遇到数据集中标签种类繁多,需要整理出指定标签种类的数据,或根据需求只获取部分数据集中的标签的时候,可以使用如下脚本处理
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import os
import xml.etree.ElementTree as ET
import shutil
#根据自己的情况修改相应的路径
ann_filepath = 'Annotations/'
img_filepath = 'JPEGImages/'
img_savepath = 'test/JPEGImages/'
ann_savepath = 'test/Annotations/'
if not os.path.exists(img_savepath):
os.mkdir(img_savepath)
if not os.path.exists(ann_savepath):
os.mkdir(ann_savepath)
classes = ['aeroplane', 'bicycle', 'tvmonitor'] #这里是需要提取的类别
def save_annotation(file):
tree = ET.parse(ann_filepath + '/' + file)
root = tree.getroot()
result = root.findall("object")
bool_num = 0
for obj in result:
if obj.find("name").text not in classes:
root.remove(obj)
else:
bool_num = 1
if bool_num:
tree.write(ann_savepath + file)
return True
else:
return False
def save_images(file):
name_img = img_filepath + os.path.splitext(file)[0] + ".jpg"
shutil.copy(name_img, img_savepath)
#文本文件名自己定义,主要用于生成相应的训练或测试的txt文件
with open('test/test.txt', 'a') as file_txt:
file_txt.write(os.path.splitext(file)[0])
file_txt.write("\n")
return True
if __name__ == '__main__':
for f in os.listdir(ann_filepath):
if save_annotation(f):
save_images(f)
2.2、 修改文件名后缀
在数据处理时,如果数据是由很多方面整合而来的,那么数据可能存在不统一的情况,甚至jpg和JPG也会出现,还有jpeg、png等需要统一为jpg格式。
#!/usr/bin/env python3.8
# -*- coding: utf-8 -*-
import os
# 想要更改图片所在的根目录
rootdir = r"F:\20240104-小动物闯入数据集\Annotations"
# 获取目录下文件名清单
files = os.listdir(rootdir)
# 对文件名清单里的每一个文件名进行处理
for filename in files:
portion = os.path.splitext(filename) # portion为名称和后缀分离后的列表 #os.path.splitext()将文件名和扩展名分开
if portion[1] == ".jpg": # 如果为tiff则更改名字
newname = portion[0] + ".xml" # 要改的新后缀 #改好的新名字
print(filename) # 打印出要更改的文件名
os.chdir(rootdir) # 修改工作路径
os.rename(filename, newname) # 在工作路径下对文件名重新命名
2.3、 删除宽高为0的xml
import os
import xml.etree.ElementTree as ET
"""
查找指定文件夹下所有.xml文件的width元素及其值。
"""
def search_width_element(folder_path):
for filename in os.listdir(folder_path):
if filename.endswith('.xml'):
xml_path = os.path.join(folder_path, filename)
tree = ET.parse(xml_path)
root = tree.getroot()
for element in root.iter():
if element.tag == 'width' and element.text == '0':
print(f"在文件 '{filename}' 中找到符合格式的元素:")
print(ET.tostring(element, encoding='utf-8').decode('utf-8'))
os.remove(xml_path)
print(f"已删除文件 '{filename}'。")
print('-' * 40)
folder_path = r'D:\数据集\通用\摔倒\fall\111\Annotations'
# search_width_element(folder_path)
"""
删除文件夹中所有宽高为0的xml文件
"""
folder1_path = r"D:\数据集\通用\摔倒\fall\111\Annotations"
folder2_path = r"D:\数据集\通用\摔倒\fall\111\images"
# 获取folder1中的文件名(不包括扩展名)列表
folder1_files = [os.path.splitext(file)[0] for file in os.listdir(folder1_path)]
# 获取folder2中的文件名(不包括扩展名)列表
folder2_files = [os.path.splitext(file)[0] for file in os.listdir(folder2_path)]
# 遍历folder2中的文件
for file_name in folder2_files:
if file_name not in folder1_files:
xml_path = os.path.join(folder2_path, file_name + ".xml")
jpg_path = os.path.join(folder2_path, file_name + ".jpg")
if os.path.exists(xml_path):
print(f"Removing xml file: {file_name}.xml")
os.remove(xml_path)
if os.path.exists(jpg_path):
print(f"Removing jpg file: {file_name}.jpg")
os.remove(jpg_path)
2.4、 删除文件名中存在的中文符号
import os
import re
import shutil
# 源文件夹路径
src_folder = r'D:\pythonProject\1scripts\images\test'
# 新建的目标文件夹路径
target_folder = r'D:\pythonProject\1scripts\images\test_chinese'
# 如果目标文件夹不存在,则创建它
if not os.path.exists(target_folder):
os.makedirs(target_folder)
# 定义一个正则表达式,用于匹配包含中文和括号的文件名
filename_pattern = re.compile(r'[\u4e00-\u9fff()]+') # 匹配Unicode范围内的中文字符和括号
# 遍历源文件夹中的所有文件和子文件夹
for dir_path, dir_names, file_names in os.walk(src_folder):
for file_name in file_names:
if filename_pattern.search(file_name): # 如果文件名匹配正则表达式
src_file_path = os.path.join(dir_path, file_name)
target_file_path = os.path.join(target_folder, file_name)
# 将包含中文字符或括号的文件移动到目标文件夹
shutil.move(src_file_path, target_file_path)
# 注意:这段代码会将源目录下满足条件的所有文件移动到目标文件夹,而不是复制。
# 如果你需要复制文件,请使用shutil.copy2()函数替换shutil.move()函数。
2.5、 批量重命名
#!/usr/bin/env python3.8
# -*- coding: utf-8 -*-
"""
function: 检查数据集中是否都是jpg格式
date:2023/10/26
"""
import os
import glob
pathjoin = os.path.join
prefix = '20240129_' # yjsk_sf6_6
def rename(ddir, extname, i=1):
patern = f'{ddir}/*{extname}' # 构建文件路径模式
anotfiles = glob.glob(patern) # 获取所有满足文件路径模式的文件路径
'遍历所有文件'
try:
for anotfile in anotfiles: # 遍历所有文件路径
anotname = os.path.split(anotfile)[1] # 获取文件名
imname = anotname[:-len(extname)] + '.xml' # 获取对应的xml文件名
imfile = pathjoin(ddir, imname) # 构建xml文件路径
if os.path.exists(imfile): # 判断xml文件是否存在
print(anotfile) # 输出标注文件路径
# tanotname = f'{prefix}{i}{extname}'
# tanotfile = pathjoin(ddir, tanotname)
# timname = f'{prefix}{i}.jpg' # f'{prefix}{i}.jpg'
timname = f'{prefix}{anotname[:-len(extname)]}.xml' # 构建新的xml文件名
timfile = pathjoin(ddir, timname) # 构建新的xml文件路径
# os.rename(anotfile, tanotfile)
os.rename(imfile, timfile) # 重命名xml文件
i += 1 # 计数器加1
except Exception as e:
print(e)
ddir = r'G:\20240102缺陷\xy\Annotations' # 标注文件夹路径
rename(ddir, extname='.xml') # 调用rename函数重命名xml文件
2.6、 比较两个文件夹的不同
import os
import filecmp
folder1 = r'F:\20240104-小动物闯入数据集\给你2\Annotations'
folder2 = r'F:\20240104-小动物闯入数据集\给你2\images'
files1 = set(os.listdir(folder1))
files2 = set(os.listdir(folder2))
different_files = files1.symmetric_difference(files2)
for file in different_files:
if file in files1:
file_path = os.path.join(folder1, file)
if os.path.isfile(file_path):
print(f'{file} is different in {folder1}')
if file in files2:
file_path = os.path.join(folder2, file)
if os.path.isfile(file_path):
print(f'{file} is different in {folder2}')
2.7、 百度爬取指定类型图片
import requests
import os
import re
import io
import time # 限制爬虫速度
from PIL import Image
def get_images_from_baidu(keyword, num_images, save_dir, image_format='jpg', max_size=None):
"""
从百度图片下载指定关键词的图片,并保存到指定目录中。
#:param urlip: str,代理IP的API链接。
:param keyword: str,搜索关键词。
:param num_images: int,要下载的图片数量。
:param save_dir: str,图片保存的目录。
:param image_format: str,图片格式,默认为 'jpg'。
:param max_size: tuple,限制图片的最大尺寸,格式为 (width, height)。
"""
# """获取代理IP"""
# urlip = urlip
# while 1:
# try:
# r = requests.get(urlip, timeout=10)
# except:
# continue
#
# ip = r.text.strip()
# if '请求过于频繁' in ip:
# print('IP请求频繁')
# time.sleep(1)
# continue
# break
# proxies = {
# 'https': '%s' % ip
# }
# 设置请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
# 设置请求的 URL
url = 'https://image.baidu.com/search/acjson?'
# url = '180.112.239.48:31085'
# 初始化计数器
n = 0
# 每页显示的图片数量
rn = 30
# 计算需要请求的页数
page_num = (num_images + rn - 1) // rn
# 循环遍历每一页,获取图片链接并下载保存
for pn in range(0, rn * page_num, rn):
# 请求参数
params = {
'tn': 'resultjson_com',
'logid': '7603311155072595725',
'ipn': 'rj',
'ct': 201326592,
'is': '',
'fp': 'result',
'queryWord': keyword,
'cl': 2,
'lm': -1,
'ie': 'utf-8',
'oe': 'utf-8',
'adpicid': '',
'st': -1,
'z': '',
'ic': '',
'hd': '',
'latest': '',
'copyright': '',
'word': keyword,
's': '',
'se': '',
'tab': '',
'width': '',
'height': '',
'face': 0,
'istype': 2,
'qc': '',
'nc': '1',
'fr': '',
'expermode': '',
'force': '',
'cg': '',
'pn': pn,
'rn': rn,
'gsm': '1e',
'1618827096642': ''
}
# 发送请求
response = requests.get(url=url, headers=headers, params=params)
# 检查响应是否成功
if response.ok:
print('Request success.')
# 解析响应数据并提取图片链接
response.encoding = 'utf-8'
html = response.text
image_url_list = re.findall('"thumbURL":"(.*?)",', html, re.S)
# 如果指定的目录不存在,则创建目录
if not os.path.exists(save_dir):
os.makedirs(save_dir)
# 下载并保存图片
for image_url in image_url_list:
if n >= num_images:
break
try:
# 获取图片数据
image_data = requests.get(url=image_url, headers=headers).content
# 打开图片并获取尺寸
image = Image.open(io.BytesIO(image_data))
width, height = image.size
# 如果指定了最大尺寸并且图片尺寸超过了最大尺寸,则跳过该图片
if max_size and (width > max_size[0] or height > max_size[1]):
continue
# 保存图片
with open(os.path.join(save_dir, f'{n:06d}.{image_format}'), 'wb') as fp:
fp.write(image_data)
n += 1
except (requests.RequestException, IOError) as e:
print(f'Error occurred when downloading image: {e}')
print(f'Successfully downloaded {n} images to {save_dir} directory.')
if __name__ == "__main__":
# 从终端获取用户输入的关键词和要下载的图片数量和图片格式
# urlip = '120.34.13.185:48510'
keyword = input('请输入要搜索的图片关键词:')
num_images = input('请输入要下载的图片数量:')
image_format = input('请输入要下载的图片格式(默认为 jpg):') or 'jpg'
max_width = input('请输入图片的最大宽度(默认为不限制):') or None
max_height = input('请输入图片的最大高度(默认为不限制):') or None
if max_width and max_height:
max_size = (int(max_width), int(max_height))
else:
max_size = None
num_images = int(num_images)
# 构造保存图片的目录
save_dir = os.path.join('.', 'images', keyword)
# 调用函数进行图片下载
get_images_from_baidu(keyword, num_images, save_dir, image_format, max_size)
2.8、 文件夹下图片和xml分离
import os
import shutil
def move_files(folder_path, images_folder, annotations_folder):
for item in os.listdir(folder_path):
item_path = os.path.join(folder_path, item)
if os.path.isfile(item_path):
if item.endswith('.jpg') or item.endswith('.JPG') or item.endswith('.xml'):
if item.endswith('.jpg') or item.endswith('.JPG'):
shutil.move(item_path, os.path.join(images_folder, item))
else:
shutil.move(item_path, os.path.join(annotations_folder, item))
elif os.path.isdir(item_path):
move_files(item_path, images_folder, annotations_folder)
# 文件夹路径
folder_path = r'D:\pythonproject\1222_test_txt'
# 图片文件夹路径
images_folder = r'D:\pythonproject\1222_test_txt\images'
if not os.path.exists(images_folder):
os.makedirs(images_folder)
# 标签xmk文件夹路径
annotations_folder = r'D:\pythonproject\1222_test_txt\annotations'
if not os.path.exists(annotations_folder):
os.makedirs(annotations_folder)
move_files(folder_path, images_folder, annotations_folder)
2.9(备用)、 修改xml元素宽高(提前把图片移出)
# coding=utf-8
import xml.dom.minidom
import os
path = r'D:\数据集\通用\摔倒\fall\111\Annotations' # 需要修改mxl文件夹的地址
files = os.listdir(path) # 得到文件夹下所有文件名称
for xmlFile in files: # 遍历文件夹
xml_path = os.path.join(path, xmlFile)
if not os.path.isdir(xml_path): # 判断是否是文件夹,不是文件夹才打开
dom = xml.dom.minidom.parse(xml_path) # 读入xml
root = dom.documentElement
width = root.getElementsByTagName('width')
height = root.getElementsByTagName('height')
width0 = width[0] # 原来的width
# print('width原来为',width0.firstChild.data)
width0.firstChild.data = 0 # 修改width
# print('width修改后为',width0.firstChild.data)
height0 = height[0] # 原来的height
# print('height原来为',height0.firstChild.data)
height0.firstChild.data = 0 # 修改height
# print('height修改后为',height0.firstChild.data)
with open(xml_path, 'w') as fh:
dom.writexml(fh)
print('{}写入width和height成功!'.format(xmlFile))
3、拆分数据集
3.1、 (A-txt-linux)splitDataset
import os
import random
trainval_percent = 0.8
train_percent = 0.8
xmlfilepath = r'/home/1212_tongyong/'
# xmlfilepath = r'/home/1212_tongyong/Annotations/'
txtsavepath = r'/home/1212_tongyong/ImageSets/'
total_xml = os.listdir(xmlfilepath)
num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)
ftrainval = open(r'/home/1212_tongyong/ImageSets/trainval.txt', 'w')
ftest = open(r'/home/1212_tongyong/ImageSets/test.txt', 'w')
ftrain = open(r'/home/1212_tongyong/ImageSets/train.txt', 'w')
fval = open(r'/home/1212_tongyong/ImageSets/val.txt', 'w')
for i in list:
name = total_xml[i][:-4] + '\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
3.2、 (A-txt-linux)splitDataset
import os
import random
'''
只通过xml或labels文件夹生成ImageSets文件夹下的txt文件,不涉及图片的复制和移动
'''
trainval_percent = 0.8
train_percent = 0.8
xmlfilepath = r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\labels'
# xmlfilepath = r'D:\数据集\通用\小动物入侵\mouse\Annotations'
txtsavepath = r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets'
total_xml = os.listdir(xmlfilepath)
num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)
ftrainval = open(r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets\trainval.txt', 'w')
ftest = open(r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets\test.txt', 'w')
ftrain = open(r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets\train.txt', 'w')
fval = open(r'D:\pythonProject\OIDv4_ToolKit-master\OID\Dataset\train\ImageSets\val.txt', 'w')
for i in list:
name = total_xml[i][:-4] + '\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
3.3、 (B-dir-linux)splitDataset
# -*- coding: utf-8 -*-
import os
import random
import shutil
def moveimg(fileDir, tarDir):
"""
function: 数据集拆分训练集和验证集。使用时先将数据集分验证集,再分测试集
params:
fileDir: 原始数据集路径
tarDir: 目标数据集路径
return:
无
date:2023/10/26
"""
pathDir = os.listdir(fileDir) # 取图片的原始路径
filenumber = len(pathDir)
rate = 0.10
picknumber = int(filenumber * rate) # 按照rate比例从文件夹中取一定数量图片
sample = random.sample(pathDir, picknumber) # 随机选取picknumber数量的样本图片
print('sample:', sample)
print(len(sample))
for name in sample:
shutil.move(fileDir + name, tarDir + "/" + name)
return
def movelabel(file_list, file_label_train, file_label_val):
"""
function: 数据集拆分训练集和验证集
先将数据集分验证集,再分测试集
date:2023/10/26
"""
for i in file_list:
i = i.lower()
if i.endswith('.jpg'):
# filename = file_label_train + "\\" + i[:-4] + '.xml'
filename = file_label_train + "/" + i[:-4] + '.txt'
if os.path.exists(filename):
shutil.move(filename, file_label_val)
print(i + "处理成功!")
if __name__ == '__main__':
fileDir = "/home/fire/train/images" + "/" # 源图片文件夹路径
tarDir = "/home/fire/train/test_images" # 图片移动到新的文件夹路径
moveimg(fileDir, tarDir)
file_list = os.listdir(tarDir)
file_label_train = "/home/fire/train/labels" # 源图片标签路径
file_label_val = "/home/fire/train/test_labels" # 对应标签移动到新的文件夹路径
# 移动到新的文件路径
movelabel(file_list, file_label_train, file_label_val)
4、格式转换
4.1、 (A-txt-linux)XML2TXT.py
# -*- coding: utf-8 -*-
# xml解析包
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
sets = ['train', 'test', 'val']
classes = ['bjdsyc', 'bj_wkps', 'yw_nc', 'xmbhyc', 'kgg_ybh', 'gbps', 'yw_gkxfw', 'hxq_gjbs', 'bj_bpmh', 'jyz_pl', 'bj_bpps', 'sly_dmyw', 'wcaqm', 'wcgz', 'ywzt_yfyc', 'hxq_gjtps', 'xy']
# 进行归一化操作
def convert(size, box): # size:(原图w,原图h) , box:(xmin,xmax,ymin,ymax)
dw = 1./size[0] # 1/w
dh = 1./size[1] # 1/h
x = (box[0] + box[1])/2.0 # 物体在图中的中心点x坐标
y = (box[2] + box[3])/2.0 # 物体在图中的中心点y坐标
w = box[1] - box[0] # 物体实际像素宽度
h = box[3] - box[2] # 物体实际像素高度
x = x*dw # 物体中心点x的坐标比(相当于 x/原图w)
w = w*dw # 物体宽度的宽度比(相当于 w/原图w)
y = y*dh # 物体中心点y的坐标比(相当于 y/原图h)
h = h*dh # 物体宽度的宽度比(相当于 h/原图h)
return x, y, w, h # 返回 相对于原图的物体中心点的x坐标比,y坐标比,宽度比,高度比,取值范围[0-1]
def convert_annotation(image_id: object):
'''
将对应文件名的xml文件转化为label文件,xml文件包含了对应的bunding框以及图片长款大小等信息,
通过对其解析,然后进行归一化最终读到label文件中去,也就是说
一张图片文件对应一个xml文件,然后通过解析和归一化,能够将对应的信息保存到唯一一个label文件中去
labal文件中的格式:class x y w h 同时,一张图片对应的类别有多个,所以对应的bunding的信息也有多个
'''
# 对应的通过year 找到相应的文件夹,并且打开相应image_id的xml文件,其对应bund文件
in_file = open('/home/1212_tongyong/Annotations/%s.xml' % (image_id), encoding='utf-8')
print(image_id)
# 准备在对应的image_id 中写入对应的label,分别为
# <object-class> <x> <y> <width> <height>
out_file = open('/home/1212_tongyong/labels/%s.txt' % (image_id), 'w', encoding='utf-8')
# 解析xml文件
tree = ET.parse(in_file)
# 获得对应的键值对
root = tree.getroot()
# 获得图片的尺寸大小
size = root.find('size')
# 如果xml内的标记为空,增加判断条件
if size != None:
# 获得宽
w = int(size.find('width').text)
# 获得高
h = int(size.find('height').text)
# 遍历目标obj
for obj in root.iter('object'):
# 获得difficult ??
difficult = obj.find('difficult').text
# 获得类别 =string 类型
cls = obj.find('name').text
# 如果类别不是对应在我们预定好的class文件中,或difficult==1则跳过
if cls not in classes or int(difficult) == 1:
continue
# 通过类别名称找到id
cls_id = classes.index(cls)
# 找到bndbox 对象
xmlbox = obj.find('bndbox')
# 获取对应的bndbox的数组 = ['xmin','xmax','ymin','ymax']
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
print(image_id, cls, b)
# 带入进行归一化操作
# w = 宽, h = 高, b= bndbox的数组 = ['xmin','xmax','ymin','ymax']
bb = convert((w, h), b)
# bb 对应的是归一化后的(x,y,w,h)
# 生成 calss x y w h 在label文件中
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
# 返回当前工作目录
wd = getcwd()
print(wd)
for image_set in sets:
'''
对所有的文件数据集进行遍历
做了两个工作:
1.将所有图片文件都遍历一遍,并且将其所有的全路径都写在对应的txt文件中去,方便定位
2.同时对所有的图片文件进行解析和转化,将其对应的bundingbox 以及类别的信息全部解析写到label 文件中去
最后再通过直接读取文件,就能找到对应的label 信息
'''
# 先找labels文件夹如果不存在则创建
if not os.path.exists('/home/1212_tongyong/labels/'):
os.makedirs('/home/1212_tongyong/labels/')
# 读取在ImageSets/Main 中的train、test..等文件的内容
# 包含对应的文件名称
image_ids = open('/home/1212_tongyong/ImageSets/%s.txt' % (image_set)).read().strip().split()
print(image_ids)
# 打开对应的2012_train.txt 文件对其进行写入准备
list_file = open('/home/1212_tongyong/%s.txt' % (image_set), 'w')
# 将对应的文件_id以及全路径写进去并换行
for image_id in image_ids:
list_file.write('/home/1212_tongyong/images/%s.jpg\n' % (image_id))
# 调用 year = 年份 image_id = 对应的文件名_id
convert_annotation(image_id)
# 关闭文件
list_file.close()
# print(image_ids)
# print(image_id)
# print()
4.2、 (A-txt-win)XML2TXT.py
# -*- coding: utf-8 -*-
# xml解析包
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
sets = ['train', 'test', 'val']
classes = ['mouse', 'bird']
# 进行归一化操作
def convert(size, box): # size:(原图w,原图h) , box:(xmin,xmax,ymin,ymax)
dw = 1./size[0] # 1/w
dh = 1./size[1] # 1/h
x = (box[0] + box[1])/2.0 # 物体在图中的中心点x坐标
y = (box[2] + box[3])/2.0 # 物体在图中的中心点y坐标
w = box[1] - box[0] # 物体实际像素宽度
h = box[3] - box[2] # 物体实际像素高度
x = x*dw # 物体中心点x的坐标比(相当于 x/原图w)
w = w*dw # 物体宽度的宽度比(相当于 w/原图w)
y = y*dh # 物体中心点y的坐标比(相当于 y/原图h)
h = h*dh # 物体宽度的宽度比(相当于 h/原图h)
return x, y, w, h # 返回 相对于原图的物体中心点的x坐标比,y坐标比,宽度比,高度比,取值范围[0-1]
def convert_annotation(image_id: object):
'''
将对应文件名的xml文件转化为label文件,xml文件包含了对应的bunding框以及图片长款大小等信息,
通过对其解析,然后进行归一化最终读到label文件中去,也就是说
一张图片文件对应一个xml文件,然后通过解析和归一化,能够将对应的信息保存到唯一一个label文件中去
labal文件中的格式:class x y w h 同时,一张图片对应的类别有多个,所以对应的bunding的信息也有多个
'''
# 对应的通过year 找到相应的文件夹,并且打开相应image_id的xml文件,其对应bund文件
in_file = open(r'D:\pythonProject\0105-animal\mouse\Annotations\%s.xml' % (image_id), encoding='utf-8')
print(image_id)
# 准备在对应的image_id 中写入对应的label,分别为
# <object-class> <x> <y> <width> <height>
out_file = open(r'D:\pythonProject\0105-animal\mouse\labels\%s.txt' % (image_id), 'w', encoding='utf-8')
# 解析xml文件
tree = ET.parse(in_file)
# 获得对应的键值对
root = tree.getroot()
# 获得图片的尺寸大小
size = root.find('size')
# 如果xml内的标记为空,增加判断条件
if size != None:
# 获得宽
w = int(size.find('width').text)
# 获得高
h = int(size.find('height').text)
# 遍历目标obj
for obj in root.iter('object'):
# 获得difficult ??
difficult = obj.find('difficult').text
# 获得类别 =string 类型
cls = obj.find('name').text
# 如果类别不是对应在我们预定好的class文件中,或difficult==1则跳过
if cls not in classes or int(difficult) == 1:
continue
# 通过类别名称找到id
cls_id = classes.index(cls)
# 找到bndbox 对象
xmlbox = obj.find('bndbox')
# 获取对应的bndbox的数组 = ['xmin','xmax','ymin','ymax']
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
print(image_id, cls, b)
# 带入进行归一化操作
# w = 宽, h = 高, b= bndbox的数组 = ['xmin','xmax','ymin','ymax']
bb = convert((w, h), b)
# bb 对应的是归一化后的(x,y,w,h)
# 生成 calss x y w h 在label文件中
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
# 返回当前工作目录
wd = getcwd()
print(wd)
for image_set in sets:
'''
对所有的文件数据集进行遍历
做了两个工作:
1.将所有图片文件都遍历一遍,并且将其所有的全路径都写在对应的txt文件中去,方便定位
2.同时对所有的图片文件进行解析和转化,将其对应的bundingbox 以及类别的信息全部解析写到label 文件中去
最后再通过直接读取文件,就能找到对应的label 信息
'''
# 先找labels文件夹如果不存在则创建
if not os.path.exists(r'D:\pythonProject\0105-animal\mouse\labels'):
os.makedirs(r'D:\pythonProject\0105-animal\mouse\labels')
# 读取在ImageSets/Main 中的train、test..等文件的内容
# 包含对应的文件名称
image_ids = open(r'D:\pythonProject\0105-animal\mouse\ImageSets\%s.txt' % (image_set)).read().strip().split()
print(image_ids)
# 打开对应的2012_train.txt 文件对其进行写入准备
list_file = open(r'D:\pythonProject\0105-animal\mouse\%s.txt' % (image_set), 'w')
# 将对应的文件_id以及全路径写进去并换行
for image_id in image_ids:
list_file.write(r'D:\pythonProject\0105-animal\mouse\images\%s.jpg'%(image_id)+ '\n')
# 调用 year = 年份 image_id = 对应的文件名_id
convert_annotation(image_id)
# 关闭文件
list_file.close()
# print(image_ids)
# print(image_id)
# print()
4.3、 (B-dir)XML2TXT.py
# coding=utf-8
import cv2
import xml.etree.ElementTree as ET
import os
"""
分图片,不使用train.txt文件
train、val、test分别转
"""
def convert(size, box):
"""
将box转换为yolo的格式
:param size:
:param box:
:return:
"""
dw = 1. / (size[0])
dh = 1. / (size[1])
x = (box[0] + box[1]) / 2.0 - 1
y = (box[2] + box[3]) / 2.0 - 1
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 read_cls_txt(filenam):
pos = []
clsfile = open(filenam)
rows = len(clsfile.readlines())
print("There are %d lines in %s" % (rows, filenam))
if (rows == 0):
print(filenam, ": there is no lines")
return pos
with open(filenam, 'r') as file_to_read:
while True:
lines = file_to_read.readline()
if not lines:
break
pass
print("line:", lines)
pos.append(lines.rstrip("\n"))
pass
print(filenam, pos)
return pos
def xml2yolotxt():
"""
将xml文件转换为yolo的txt文件
:return:
"""
xml_path = r'D:\数据集\通用\小动物入侵\mouse\Annotations' # XML文件路径,不涉及图片
obj_img_path = r'D:\数据集\通用\小动物入侵\mouse\labels' # 转换后的txt文件保存路径
xml_path_list = []
obj_img_path_list = []
obj_img_path_loss_list = []
size_list = []
classes = read_cls_txt(r'D:\数据集\通用\小动物入侵\mouse\classes.txt')
print("classes:", classes)
for xml_name in os.listdir(xml_path):
x_name = xml_name.split(".")[0]
print("xml_name:", xml_name)
xml_path_list.append(xml_name)
obj_img_path_list.append(obj_img_path)
root = ET.parse(xml_path + "/" + xml_name).getroot()
size = root.find('size')
size_list.append(size)
w = int(size.find('width').text)
print('w:', w)
h = int(size.find('height').text)
print('h:', h)
with open(obj_img_path + "/" + x_name + "" + ".txt", "w") as out_file:
for obj in root.iter('object'):
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
print('转化失败的xml:', obj_img_path)
obj_img_path_loss_list.append(obj_img_path)
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')
print(len(xml_path_list))
print(len(obj_img_path_list))
print(len(size_list))
print(obj_img_path_loss_list)
print(len(obj_img_path_loss_list))
xml2yolotxt()