数据增强:拼接四张⼩图成为⼀张⼤图,同时合并标注信息⽬录
0. 背景:
在收集数据时,有时候我们会获取⼀批尺⼨相对较⼩的图⽚,其尺⼨之⼩甚⾄⼩于我们⽹络设定的输⼊尺⼨;这时候,如果将多个⼩图拼接⼀下,就可以让避免在预处理时⽆意义的缩放,同时,可以让每个batch中的图⽚包含更多语义信息,类似于mosaic数据增强。
因此,我决定写个脚本,将这批⼩图进⾏每组四张进⾏组合拼接,并合并每个⼩图已有的标注信息,避免合并⼤图重新标注。
1. 数据准备
⾸先需要使⽤labelImg⼯具将⼩图标注完毕,并将图⽚、xml⽂件存放到同⼀个⽂件夹下(PS:你可以按照⾃⼰的标注格式以及存放习惯,只需要改动代码中的图⽚、标注⽂件的读取路径即可)。形如:
2. 合并思路
拼接⽅式选⽤对⾓拼接,也即:四张⼩图各选⼀个顶点相接,如img1的右下⾓、img2的左下⾓、img3的右上⾓、img4的左上⾓;
随机选定四张⼩图,⽤于对⾓拼接;
获取每个⼩图的标注信息;
根据四张⼩图的尺⼨,确定出合并后⼤图的尺⼨及拼接中⼼点;
新建⼀个空的⼤图,将各个⼩图填充进去;
保存⼤图到指定路径;
修改每个⼩图中的标注信息,使之适配⼤图坐标;
合并所有⼩图的标注信息,形成⼀个统⼀的Ground Truth;
将统⼀的GTs写⼊⼀个新的xml⽂件当中,对应所保存的⼤图;
3. 拼接前后⽰例
⽤于拼接的四张⼩图:
拼接后的⼤图如下,同时,每个⼩图的标注信息也会同步到⼤图当中:
4. 完整代码
"""
Joint small images to be a big image.
"""
import os
import argparse
from PIL import Image
import numpy as np
ElementTree as ET
from bboxes2xml import bboxes2xml
def list_dir(path, list_name, suffix='xml'): # 传⼊存储的list for file in os.listdir(path):
file_path = os.path.join(path, file)getsavefilename
if os.path.isdir(file_path):
list_dir(file_path, list_name)
else:
if file_path.split('.')[-1] == suffix:
file_path = place('\\', '/')
list_name.append(file_path)
def get_bboxes(xml_path):
def get_bboxes(xml_path):
tree = ET.parse(open(xml_path, 'rb'))
root = t()
bboxes, cls = [], []
for obj in root.iter('object'):
obj_cls = obj.find('name').text
xmlbox = obj.find('bndbox')
xmin = float(xmlbox.find('xmin').text)
xmax = float(xmlbox.find('xmax').text)
ymin = float(xmlbox.find('ymin').text)
ymax = float(xmlbox.find('ymax').text)
bboxes.append([xmin, ymin, xmax, ymax])
cls.append(obj_cls)
bboxes = np.asarray(bboxes, np.int)
return bboxes, cls
def main(args):
os.w_data_path, exist_ok=True)
imgs = []
list_dir(args.raw_data_path, imgs, suffix='jpg')
def img_save_name(i, zfill=5):
file = args.filename+"_{}.jpg".format(str(i).zfill(zfill))
return os.path.w_data_path, file)
for i in range(len(imgs)):
# 随机选四张图⽚, ⽤于对⾓拼接
img_joint = np.random.choice(imgs, 4, replace=False)
img1 = Image.open(img_joint[0])
img2 = Image.open(img_joint[1])
img3 = Image.open(img_joint[2])
img4 = Image.open(img_joint[3])
bboxes1, cls1 = get_bboxes(img_joint[0].replace('.jpg', '.xml'))
bboxes2, cls2 = get_bboxes(img_joint[1].replace('.jpg', '.xml'))
bboxes3, cls3 = get_bboxes(img_joint[2].replace('.jpg', '.xml'))
bboxes4, cls4 = get_bboxes(img_joint[3].replace('.jpg', '.xml'))
# ⼤图宽⾼,及拼接中⼼点
W = max(img1.size[0], img3.size[0]) + max(img2.size[0], img4.size[0])
H = max(img1.size[1], img2.size[1]) + max(img3.size[1], img4.size[1])
P = [0, 0]
P[0] = max(img1.size[0], img3.size[0])
P[1] = max(img1.size[1], img2.size[1])
# 新建⼤图,并使⽤⼩图填充
img_big = w('RGB', (W, H))
img_big.paste(img1, (P[0]-img1.size[0], P[1]-img1.size[1]))
img_big.paste(img2, (P[0], P[1]-img2.size[1]))
img_big.paste(img3, (P[0]-img3.size[0], P[1]))
img_big.paste(img4, (P[0], P[1]))
img_big.save(img_save_name(i))
# 修改每个⼩图的Bbox,并合并所有⼦图的bbox和cls
bbox_list = []
if len(bboxes1) !=0:
bboxes1[:, 0], bboxes1[:, 2] = bboxes1[:, 0]+P[0]-img1.size[0], bboxes1[:, 2]+P[0]-img1.size[0] bboxes1[:, 1], bboxes1[:, 3] = bboxes1[:, 1]+P[1]-img1.size[1], bboxes1[:, 3]+P[1]-img1.size[1] bbox_list.append(bboxes1)
if len(bboxes2) != 0:
bboxes2[:, 0], bboxes2[:, 2] = bboxes2[:, 0]+P[0], bboxes2[:, 2]+P[0]
bboxes2[:, 1], bboxes2[:, 3] = bboxes2[:, 1]+P[1]-img2.size[1], bboxes2[:, 3]+P[1]-img2.size[1] bbox_list.append(bboxes2)
if len(bboxes3) != 0:
bboxes3[:, 0], bboxes3[:, 2] = bboxes3[:, 0]+P[0]-img3.size[0], bboxes3[:, 2]+P[0]-img3.size[0] bboxes3[:, 1], bboxes3[:, 3] = bboxes3[:, 1]+P[1], bboxes3[:, 3]+P[1]
bboxes3[:, 1], bboxes3[:, 3] = bboxes3[:, 1]+P[1], bboxes3[:, 3]+P[1]
bbox_list.append(bboxes3)
if len(bboxes4) != 0:
bboxes4[:, 0], bboxes4[:, 2] = bboxes4[:, 0]+P[0], bboxes4[:, 2]+P[0]
bboxes4[:, 1], bboxes4[:, 3] = bboxes4[:, 1]+P[1], bboxes4[:, 3]+P[1]
bbox_list.append(bboxes4)
if len(bbox_list) != 0:
bboxes = np.vstack(tuple(bbox_list))
cls = cls1 + cls2 + cls3 + cls4
gts = [[c] + b.tolist() for c, b in zip(cls, bboxes)]
bboxes2xml(w_data_path.split('/')[-1],
img_name=img_save_name(i).replace('\\', '/').split('/')[-1].replace('.jpg', ''),
width=W, height=H,
gts=gts, xml_save_w_data_path)
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("--raw_data_path", default="raw_data_path", type=str,
help="raw dataset files")
parser.add_argument("--new_data_path", default="new_data_path", type=str,
help="generated new dataset files")
parser.add_argument("--filename", default="your_filename", type=str, help="save name") return parser.parse_args()
if __name__ == '__main__':
args = parse_args()
main(args)
其中,bboxes2xml⽅法定义在bboxes2xml.py⽂件中:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论