1. 批量修改xml标注框坐标
- 用训练的模型生成了一些标注框,但是都有些往左上角偏移,一张图里的框又太多,一张张手动调比较费时,根据网上的轮改了改,读取xml中xmin,ymin,xmax,ymax对应节点并对其值进行微调,调整过后标注框的位置就比较合适了,完整代码如下:
- 注意这个是直接对xml进行修改了,为避免出错可以提前备份一份
import os
import os.path
import xml.dom.minidom
path = r'E:\picture'
files = os.listdir(path)
for xmlFile in files:
if xmlFile[-4:] in ['.xml']:
dom = xml.dom.minidom.parse(os.path.join(path, xmlFile))
root = dom.documentElement
xmin = root.getElementsByTagName('xmin')
ymin = root.getElementsByTagName('ymin')
xmax = root.getElementsByTagName('xmax')
ymax = root.getElementsByTagName('ymax')
for i in range(len(xmin)):
a = xmin[i].firstChild.data
xmin[i].firstChild.data = int(a)+4
for j in range(len(ymin)):
b = ymin[j].firstChild.data
ymin[j].firstChild.data = int(b)+3
for k in range(len(xmax)):
c = xmax[k].firstChild.data
xmax[k].firstChild.data = int(c)+4
for l in range(len(ymax)):
d = ymax[l].firstChild.data
ymax[l].firstChild.data = int(d)+3
with open(os.path.join(path, xmlFile), 'w') as fh:
dom.writexml(fh)
print('修改标注框坐标成功')
2. 批量修改xml里的类别名称
- 分为以下两种情况,其实跟上方修改坐标差不多的思路,找到对应的节点进行修改
- 1.将文件夹📂下所有的类别替换为同一个名称
- 2.将特定类别的标注框名称进行替换
- 注意这个是直接对xml进行修改了,为避免出错可以提前备份一份
2.1 所有的类别替换
import os
import os.path
import xml.dom.minidom
path = r'E:\picture'
files = os.listdir(path)
for xmlFile in files:
if xmlFile[-4:] in ['.xml']:
dom = xml.dom.minidom.parse(os.path.join(path, xmlFile))
root = dom.documentElement
name = root.getElementsByTagName('name')
for i in range(len(name)):
name[i].firstChild.data = 'c'
with open(os.path.join(path, xmlFile), 'w',encoding='UTF-8') as fh:
dom.writexml(fh,encoding='UTF-8')
print('全部类别名称修改完成')
2.2 指定类别的替换
import os
import os.path
import xml.dom.minidom
path = r'E:\picture'
files = os.listdir(path)
for xmlFile in files:
if xmlFile[-4:] in ['.xml']:
dom = xml.dom.minidom.parse(os.path.join(path, xmlFile))
root = dom.documentElement
name = root.getElementsByTagName('name')
for i in range(len(name)):
if name[i].firstChild.data =='a':
name[i].firstChild.data = 'b'
with open(os.path.join(path, xmlFile), 'w',encoding='UTF-8') as fh:
dom.writexml(fh,encoding='UTF-8')
print('指定类别名称替换完成')
3. 子节点的删除
- 利用以上文件对xml进行修改后发现有一些图片用labelImg打开没有标注框,检查后发现没有框的都是path路径中有中文的,当时就想着把path这一节点删除,
import xml.etree.ElementTree as ET
import os
path1 = "E:/picture/"
save_path = "E:/picture/123/"
files = os.listdir(path1)
for xmlFile in files:
if xmlFile.endswith('.xml'):
tree = ET.ElementTree(file=path1 + xmlFile)
root = tree.getroot()
for path in root.findall('path'):
root.remove(path)
tree.write(save_path + xmlFile)
print('子节点删除完成')
- path子节点确实被删除了,但是folder节点乱码了,不太可取
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d70c3b950283282f28a3ec7e20bd37bb.png#pic_center)
4. 批量删除属性列表
- 感觉删除path子节点也不太好,再看了看发现原来修改完后编译头发生了变化,原本是这样子的
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c76af259a875729907d0abdcfffec9d0.png#pic_center)
- 用了上方的替换代码后变成了这样,当时保存时还没加
encoding='UTF-8'
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/67b49ff3e25f0266f7071462422c0080.png#pic_center)
- 百度后发现应该要加
encoding='UTF-8'
(所以上方代码保存时加了dom.writexml(fh,encoding='UTF-8')
),但是加了之后annotation没有另起一行,并且labelImg还是不显示框
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/54fac5585dffdfebf858b3adb0351908.png#pic_center)
- 所以最后就选择把第一行的属性列表删掉,labelImg显示框了,删除后如下图
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b8718a354cd81d45a985d0043f34d82b.png#pic_center)
- 删除属性列表代码如下,主要也是查找替换
import os
xmldir = r'E:\picture'
savedir = r'E:\picture\123'
xmllist = os.listdir(xmldir)
for xml in xmllist:
if '.xml' in xml:
fo = open(savedir + '/' + '{}'.format(xml), 'w', encoding='utf-8')
print('{}'.format(xml))
fi = open(xmldir + '/' + '{}'.format(xml), 'r', encoding='utf-8')
content = fi.readlines()
for line in content:
line = line.replace('<?xml version="1.0" encoding="UTF-8"?>', '')
fo.write(line)
fo.close()
print('替换成功')
5. 根据xml切割图片
import cv2
import xml.etree.ElementTree as ET
import os
from pathlib import Path
import numpy as np
import random
img_path = r'E:\picture'
xml_path = r'E:\picture'
obj_img_path = r'E:\picture\cut'
Numpic = {}
for img_file in os.listdir(img_path):
if img_file[-4:] in ['.png', '.jpg']:
img_filename = os.path.join(img_path, img_file)
img_cv = cv2.imread(img_filename)
img_name = (os.path.splitext(img_file)[0])
xml_name = xml_path + '\\' + '%s.xml' % img_name
if os.path.exists(xml_name):
root = ET.parse(xml_name).getroot()
for obj in root.iter('object'):
name = obj.find('name').text
xmlbox = obj.find('bndbox')
x0 = xmlbox.find('xmin').text
y0 = xmlbox.find('ymin').text
x1 = xmlbox.find('xmax').text
y1 = xmlbox.find('ymax').text
obj_img = img_cv[int(y0):int(y1), int(x0):int(x1)]
Numpic.setdefault(name, 0)
Numpic[name] += 1
my_file = Path(obj_img_path + '\\' + name)
if 1 - my_file.is_dir():
os.mkdir(obj_img_path + '\\' + str(name))
cv2.imencode('.jpg', obj_img)[1].tofile(obj_img_path + '\\' + name + '\\' +img_name+'_'+ '%03d' % (Numpic[name]) + '.jpg')
import xml.etree.ElementTree as ET
import cv2
import os
from lxml import etree
from tqdm import tqdm
class GEN_Annotations:
def __init__(self, filename):
self.root = etree.Element("annotation")
child1 = etree.SubElement(self.root, "folder")
child1.text = "a"
child2 = etree.SubElement(self.root, "filename")
child2.text = filename
child3 = etree.SubElement(self.root, "source")
child4 = etree.SubElement(child3, "annotation")
child4.text = "c"
child5 = etree.SubElement(child3, "database")
child5.text = "Unknown"
def set_size(self, witdh, height, channel):
size = etree.SubElement(self.root, "size")
widthn = etree.SubElement(size, "width")
widthn.text = str(witdh)
heightn = etree.SubElement(size, "height")
heightn.text = str(height)
channeln = etree.SubElement(size, "depth")
channeln.text = str(channel)
def savefile(self, filename):
tree = etree.ElementTree(self.root)
tree.write(filename, pretty_print=True, xml_declaration=False, encoding='utf-8')
def add_pic_attr(self, label, xmin, ymin, xmax, ymax):
object = etree.SubElement(self.root, "object")
namen = etree.SubElement(object, "name")
namen.text = label
bndbox = etree.SubElement(object, "bndbox")
xminn = etree.SubElement(bndbox, "xmin")
xminn.text = str(xmin)
yminn = etree.SubElement(bndbox, "ymin")
yminn.text = str(ymin)
xmaxn = etree.SubElement(bndbox, "xmax")
xmaxn.text = str(xmax)
ymaxn = etree.SubElement(bndbox, "ymax")
ymaxn.text = str(ymax)
def get_ext(w,h,b):
b1 = []
b1.append(max(0, b[0]))
b1.append(max(0, b[1]))
b1.append(min(w, b[2]))
b1.append(min(h, b[3]))
return b1
path_img = r'E:\picture'
path_xml = r'E:\picture'
out_path_img =r'E:\picture\jpg'
out_path_xml = r'E:\picture\ann'
def makedir(*new_dir):
for dir in new_dir:
if not os.path.exists(dir):
os.makedirs(dir)
makedir(out_path_img, out_path_xml)
img_dir = os.listdir(path_img)
xml_dir = os.listdir(path_xml)
idx = 0
clss = ['a','b','c']
for i in tqdm(range(len(img_dir))):
if img_dir[i][-4:] in ['.png', '.jpg']:
print(i)
xml_path = os.path.join(path_xml, img_dir[i][:-3]+'xml')
jpg_file = os.path.join(path_img, img_dir[i])
if not os.path.exists(xml_path):
continue
im = cv2.imread(jpg_file)
tree = ET.parse(xml_path)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
is_show = True
for obj in root.iter('object'):
cls = obj.find('name').text
if cls in clss:
xmlbox = obj.find('bndbox')
b = [float(xmlbox.find('xmin').text), float(xmlbox.find('ymin').text), float(xmlbox.find('xmax').text),
float(xmlbox.find('ymax').text)]
b1 = get_ext(w, h, b)
xmin, ymin, xmax, ymax = int(b[0]-b1[0] ), int(b[1]-b1[1]), int(b[2]-b1[0]), int(b[3] - b1[1])
new_img = im[int(b1[1]):int(b1[3]), int(b1[0]):int(b1[2])].copy()
idx += 1
new_img_path = os.path.join(out_path_img, img_dir[i][:-4]+'_'+str(idx).zfill(6)+".jpg")
new_xml_path = os.path.join(out_path_xml, img_dir[i][:-4]+'_'+str(idx).zfill(6)+".xml")
cv2.imencode('.jpg', new_img)[1].tofile(new_img_path)
print(new_img_path)
anno = GEN_Annotations(new_img_path)
nh, nw, nc = new_img.shape
anno.set_size(nw, nh, nc)
anno.add_pic_attr(cls, int(b1[0]), int(b1[1]), int(b1[2]), int(b1[3]))
anno.savefile(new_xml_path)