昨天我分享了有關標註檔案格式的批次轉換方法。今天要分享的是如果您的訓練影像數量不足,想要擴充資料,該怎麼辦?當然,一種方法是多蒐集更多影像,但這通常需要額外的時間和努力,特別是在標註這些影像的過程中。
因此,我考慮了如何能從現有影像生成更多的訓練影像,尤其在某些領域的影像蒐集相當困難時。一個方法是參考分類任務中常見的技巧,例如對影像進行平移、旋轉、縮放等變換,用以生成更多的訓練樣本。
雖然上述的方法能夠生成更多的影像,但仍需要耗費時間在手動標註影像上。因此今天要分享給各位的是,能藉由現有圖片以及標註檔,自動生成影像以及標註檔出來的方法。
#本程式碼用以將圖片連同其標註檔一起augmentation
#Library import
from bs4 import BeautifulSoup
import imgaug as ia
import imageio
from imgaug.augmentables.bbs import BoundingBox,BoundingBoxesOnImage
from imgaug import augmenters as iaa
from lxml import etree
from tqdm import trange
import glob
#Create functions
class CreateAnnotations:
def __init__(self,foldername,filename):
self.root=etree.Element("annotation")
child1=etree.SubElement(self.root,"folder")
child1.text=foldername
child2=etree.SubElement(self.root,"filename")
child2.text=filename
child3=etree.SubElement(self.root,"path")
child3.text=filename
child4=etree.SubElement(self.root,"source")
child5=etree.SubElement(child4,"database")
child5.text="Unknown"
def set_size(self,imgshape):
(height,witdh,channel)=imgshape
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)
#資料擴增的方法,可以自行添加新的擴增方式
seq=iaa.Sequential([
iaa.Affine(rotate=(-60, 60)),
iaa.Fliplr(0.5),
#iaa.GaussianBlur(sigma=(0,3.0)),
#iaa.AdditiveGaussianNoise(scale=(10, 60)),
iaa.Crop(percent=(0, 0.2)),
iaa.Add(50, per_channel=True),
iaa.Sharpen(alpha=0.5),
iaa.Affine(shear=(-16, 16))
])
#開始擴增
#原檔影像位置
file_dir='./example/images/'
img_list=glob.glob(file_dir+'*.jpg')
n=len(img_list)
print('原圖共有'+str(n)+'張影像')
#for i in trange(n):
for i in img_list:
i=i.replace("\\", "/")
i=i[17:-4]
print(i)
soup =BeautifulSoup(open('./example/labels/'+str(i)+".xml",encoding="utf-8"),"lxml")#原標註檔
image=imageio.imread('./example/images/'+str(i)+".jpg")#原影像,須注意副檔名
bbsOnImg=[]
for objects in soup.find_all(name="object"):
object_name=str(objects.find(name="name").string)
xmin=int(objects.xmin.string)
ymin=int(objects.ymin.string)
xmax=int(objects.xmax.string)
ymax=int(objects.ymax.string)
bbsOnImg.append(BoundingBox(x1=xmin,x2=xmax,y1=ymin,y2=ymax,label=object_name))
bbs=BoundingBoxesOnImage(bbsOnImg,shape=image.shape)
#這邊舉例一張影像擴充十張
for j in range(10):
j=j+1
image_aug,bbs_aug=seq(image=image,bounding_boxes=bbs)
bbs_aug_clip=bbs_aug.clip_out_of_image()
#擴充出來的標註檔資料夾存放位置
foldername = "./example/aug_labels/"
filename = str(i)+"_"+str(j)+".jpg"
anno = CreateAnnotations(foldername, filename)
anno.set_size(image_aug.shape)
for index,bb in enumerate(bbs_aug_clip):
xmin = int(bb.x1)
ymin = int(bb.y1)
xmax = int(bb.x2)
ymax = int(bb.y2)
label = str(bb. label)
anno.add_pic_attr(label, xmin, ymin, xmax, ymax)
anno.savefile("{}{}.xml".format(foldername,filename.split(".")[0]))
#擴充出來的影像資料夾存放位置
aug_img_path='./example/aug_images/'
imageio.imsave(aug_img_path+filename, image_aug)
pip install beautifulsoup4 imgaug
python aug_with_label.py
請問上述的語法能夠用在YOLO格式嗎
可以喔,需要額外注意的是:
本資料擴增方法: 原圖標註檔為xml格式,經由本文code後會生成擴增後的圖片以及其標註檔,但因為生成的同樣為xml格式,故需要前一天文章提供的xml轉txt格式的code,如此一來就可以將轉好的圖片以及txt格式標註檔輸入至模型進行訓練了。