# Agumentation Images (請注意:路徑) ## 要import的模組 ```python= import imgaug as ia ia.seed(1) # imgaug uses matplotlib backend for displaying images %matplotlib inline from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage from imgaug import augmenters as iaa # imageio library will be used for image input/output import imageio import pandas as pd import numpy as np import re import os import glob # this library is needed to read XML files for converting it into CSV import xml.etree.ElementTree as ET import shutil import numpy as np import numpy import cv2 import codecs import random from PIL import ImageFile ImageFile.LOAD_TRUNCATED_IMAGES = True ``` ## 從xml檔轉成DataFrame ```python= def xml_to_csv(path): xml_list = [] for xml_file in glob.glob(path + '/*.xml'): tree = ET.parse(xml_file) root = tree.getroot() for member in root.findall('object'): try: value = (root.find('filename').text, int(root.find('size')[0].text), int(root.find('size')[1].text), member[0].text, int(member[4][0].text), int(member[4][1].text), int(member[4][2].text), int(member[4][3].text) ) xml_list.append(value) except: pass column_name = ['filename', 'width', 'height', 'class', 'xmin', 'ymin', 'xmax', 'ymax'] xml_df = pd.DataFrame(xml_list, columns=column_name) return xml_df labels_df = xml_to_csv('VOCdevkit/VOC2007/Annotations/') ``` ## 修正圖片大小(大於等於600做修正) **將bbox轉入DataFrame** ```python= # function to convert BoundingBoxesOnImage object into DataFrame def bbs_obj_to_df(bbs_object): # convert BoundingBoxesOnImage object into array bbs_array = bbs_object.to_xyxy_array() # convert array into a DataFrame ['xmin', 'ymin', 'xmax', 'ymax'] columns df_bbs = pd.DataFrame(bbs_array, columns=['xmin', 'ymin', 'xmax', 'ymax']) return df_bbs ``` **調整圖片高度和寬度的設定** ```python= height_resize = iaa.Sequential([ iaa.Resize({"height": 600, "width": 'keep-aspect-ratio'}) ]) width_resize = iaa.Sequential([ iaa.Resize({"height": 'keep-aspect-ratio', "width": 600}) ]) ``` **做修正圖片的程式碼** ```python= def resize_imgaug(df, images_path, aug_images_path, total_images_path, image_prefix): # create data frame which we're going to populate with augmented image info aug_bbs_xy = pd.DataFrame(columns= ['filename','width','height','class', 'xmin', 'ymin', 'xmax', 'ymax'] ) grouped = df.groupby('filename') for filename in df['filename'].unique(): # Get separate data frame grouped by file name group_df = grouped.get_group(filename) group_df = group_df.reset_index() group_df = group_df.drop(['index'], axis=1) # The only difference between if and elif statements below is the use of height_resize and width_resize augmentors # deffined previously. # If image height is greater than or equal to image width # AND greater than 600px perform resizing augmentation shrinking image height to 600px. if group_df['height'].unique()[0] >= group_df['width'].unique()[0] and group_df['height'].unique()[0] > 600: # read the image image = imageio.imread(images_path+filename) # get bounding boxes coordinates and write into array bb_array = group_df.drop(['filename', 'width', 'height', 'class'], axis=1).values # pass the array of bounding boxes coordinates to the imgaug library bbs = BoundingBoxesOnImage.from_xyxy_array(bb_array, shape=image.shape) # apply augmentation on image and on the bounding boxes image_aug, bbs_aug = height_resize(image=image, bounding_boxes=bbs) # write augmented image to a total file imageio.imwrite(total_images_path+image_prefix+filename, image_aug) # write augmented image to a file imageio.imwrite(aug_images_path+image_prefix+filename, image_aug) # create a data frame with augmented values of image width and height info_df = group_df.drop(['xmin', 'ymin', 'xmax', 'ymax'], axis=1) for index, _ in info_df.iterrows(): info_df.at[index, 'width'] = image_aug.shape[1] info_df.at[index, 'height'] = image_aug.shape[0] # rename filenames by adding the predifined prefix info_df['filename'] = info_df['filename'].apply(lambda x: image_prefix+x) # create a data frame with augmented bounding boxes coordinates using the function we created earlier bbs_df = bbs_obj_to_df(bbs_aug) # concat all new augmented info into new data frame aug_df = pd.concat([info_df, bbs_df], axis=1) # append rows to aug_bbs_xy data frame aug_bbs_xy = pd.concat([aug_bbs_xy, aug_df]) # if image width is greater than image height # AND greater than 600px perform resizing augmentation shrinking image width to 600px elif group_df['width'].unique()[0] > group_df['height'].unique()[0] and group_df['width'].unique()[0] > 600: # read the image image = imageio.imread(images_path+filename) # get bounding boxes coordinates and write into array bb_array = group_df.drop(['filename', 'width', 'height', 'class'], axis=1).values # pass the array of bounding boxes coordinates to the imgaug library bbs = BoundingBoxesOnImage.from_xyxy_array(bb_array, shape=image.shape) # apply augmentation on image and on the bounding boxes image_aug, bbs_aug = width_resize(image=image, bounding_boxes=bbs) # write augmented image to a total file imageio.imwrite(total_images_path+image_prefix+filename, image_aug) # write augmented image to a file imageio.imwrite(aug_images_path+image_prefix+filename, image_aug) # create a data frame with augmented values of image width and height info_df = group_df.drop(['xmin', 'ymin', 'xmax', 'ymax'], axis=1) for index, _ in info_df.iterrows(): info_df.at[index, 'width'] = image_aug.shape[1] info_df.at[index, 'height'] = image_aug.shape[0] # rename filenames by adding the predifined prefix info_df['filename'] = info_df['filename'].apply(lambda x: image_prefix+x) # create a data frame with augmented bounding boxes coordinates using the function we created earlier bbs_df = bbs_obj_to_df(bbs_aug) # concat all new augmented info into new data frame aug_df = pd.concat([info_df, bbs_df], axis=1) # append rows to aug_bbs_xy data frame aug_bbs_xy = pd.concat([aug_bbs_xy, aug_df]) # append image info without any changes if it's height and width are both less than 600px else: image = imageio.imread(images_path+filename) imageio.imwrite(total_images_path+image_prefix+filename, image) aug_bbs_xy = pd.concat([aug_bbs_xy, group_df]) # return dataframe with updated images and bounding boxes annotations aug_bbs_xy = aug_bbs_xy.reset_index() aug_bbs_xy = aug_bbs_xy.drop(['index'], axis=1) return aug_bbs_xy resized_images_df = resize_imgaug(labels_df, 'VOCdevkit/VOC2007/JPEGImages/', 'VOCdevkit/VOC2007/JPEGImages/', 'VOCdevkit/VOC2007/All_Images/', '') resized_images_df.sort_values(by=['filename'], inplace=True) ``` ## 強化圖片 **調整圖片斜度、平移...等設定** ```python= aug = iaa.SomeOf(2, [ iaa.Affine(scale=(0.5, 1.5)), iaa.Affine(rotate=(-60, 60)), iaa.Affine(translate_percent={"x":(-0.3, 0.3),"y":(-0.3, 0.3)}), iaa.Fliplr(1), iaa.Multiply((0.5, 1.5)), iaa.GaussianBlur(sigma=(1.0, 3.0)), iaa.AdditiveGaussianNoise(scale=(0.03*255, 0.05*255)) ]) ``` **做強化圖片的程式碼** ```python= def image_aug(df, images_path, aug_images_path, total_images_path, image_prefix, augmentor): # create data frame which we're going to populate with augmented image info aug_bbs_xy = pd.DataFrame(columns= ['filename','width','height','class', 'xmin', 'ymin', 'xmax', 'ymax'] ) grouped = df.groupby('filename') for filename in df['filename'].unique(): # get separate data frame grouped by file name group_df = grouped.get_group(filename) group_df = group_df.reset_index() group_df = group_df.drop(['index'], axis=1) # read the image image = imageio.imread(images_path+filename) # get bounding boxes coordinates and write into array bb_array = group_df.drop(['filename', 'width', 'height', 'class'], axis=1).values # pass the array of bounding boxes coordinates to the imgaug library bbs = BoundingBoxesOnImage.from_xyxy_array(bb_array, shape=image.shape) # apply augmentation on image and on the bounding boxes image_aug, bbs_aug = augmentor(image=image, bounding_boxes=bbs) # disregard bounding boxes which have fallen out of image pane bbs_aug = bbs_aug.remove_out_of_image() # clip bounding boxes which are partially outside of image pane bbs_aug = bbs_aug.clip_out_of_image() # don't perform any actions with the image if there are no bounding boxes left in it if re.findall('Image...', str(bbs_aug)) == ['Image([]']: pass # otherwise continue else: # write augmented image to a total file imageio.imwrite(total_images_path+image_prefix+filename, image_aug) # write augmented image to a file imageio.imwrite(aug_images_path+image_prefix+filename, image_aug) # create a data frame with augmented values of image width and height info_df = group_df.drop(['xmin', 'ymin', 'xmax', 'ymax'], axis=1) for index, _ in info_df.iterrows(): info_df.at[index, 'width'] = image_aug.shape[1] info_df.at[index, 'height'] = image_aug.shape[0] # rename filenames by adding the predifined prefix info_df['filename'] = info_df['filename'].apply(lambda x: image_prefix+x) # create a data frame with augmented bounding boxes coordinates using the function we created earlier bbs_df = bbs_obj_to_df(bbs_aug) # concat all new augmented info into new data frame aug_df = pd.concat([info_df, bbs_df], axis=1) # append rows to aug_bbs_xy data frame aug_bbs_xy = pd.concat([aug_bbs_xy, aug_df]) # return dataframe with updated images and bounding boxes annotations aug_bbs_xy = aug_bbs_xy.reset_index() aug_bbs_xy = aug_bbs_xy.drop(['index'], axis=1) return aug_bbs_xy augmented_images_df = image_aug(resized_images_df, 'VOCdevkit/VOC2007/JPEGImages/', 'VOCdevkit/VOC2007/Aug_Images/', 'VOCdevkit/VOC2007/All_Images/', 'aug1_', aug) augmented_images_df.sort_values(by=['filename'], inplace=True) ``` ## 原本+強化的DataFrame匯集成一個CSV檔 ```python= total_images_df = resized_images_df.append(augmented_images_df) total_images_df = total_images_df.reset_index() total_images_df = total_images_df.drop(['index', 'width', 'height'], axis=1) new_header = total_images_df.iloc[0] total_images_df = total_images_df[1:] total_images_df.columns = new_header total_images_df.to_csv('all_labels.csv', index=False) ``` ## CSV檔轉成XML檔 ```python= total_images_df = resized_images_df.append(augmented_images_df) total_images_df = total_images_df.reset_index() total_images_df = total_images_df.drop(['index', 'width', 'height'], axis=1) new_header = total_images_df.iloc[0] total_images_df = total_images_df[1:] total_images_df.columns = new_header total_images_df.to_csv('all_labels.csv', index=False) total_csv_annotations = {} annotations = pd.read_csv('all_labels.csv',header=None).values for annotation in annotations: key = annotation[0].split(os.sep)[-1] value = np.array([annotation[1:]]) if key in total_csv_annotations.keys(): total_csv_annotations[key] = np.concatenate((total_csv_annotations[key],value),axis=0) else: total_csv_annotations[key] = value for filename,label in total_csv_annotations.items(): #embed() height, width, channels = cv2.imread('VOCdevkit/VOC2007/All_Images/' + filename).shape #embed() with codecs.open('VOCdevkit/VOC2007/All_Annotations/'+filename.replace(".jpg",".xml"),"w","utf-8") as xml: xml.write('<annotation>\n') xml.write('\t<folder>' + str(label[0][0]) + '</folder>\n') xml.write('\t<filename>' + filename + '</filename>\n') xml.write('\t<path>') xml.write('\t\t<database>Unknown</database>') xml.write('\t</path>\n') xml.write('\t<source>\n') xml.write('\t\t<database>Unknown</database>\n') xml.write('\t</source>\n') xml.write('\t<size>\n') xml.write('\t\t<width>'+ str(width) + '</width>\n') xml.write('\t\t<height>'+ str(height) + '</height>\n') xml.write('\t\t<depth>' + str(channels) + '</depth>\n') xml.write('\t</size>\n') xml.write('\t\t<segmented>0</segmented>\n') if isinstance(label,float): ## 空白 xml.write('</annotation>') continue for label_detail in label: labels = label_detail #embed() xmin = int(0 if math.isnan(labels[1]) == True else labels[1]) ymin = int(0 if math.isnan(labels[2]) == True else labels[2]) xmax = int(0 if math.isnan(labels[3]) == True else labels[3]) ymax = int(0 if math.isnan(labels[4]) == True else labels[4]) label_ = labels[0] if xmax <= xmin: pass elif ymax <= ymin: pass else: xml.write('\t<object>\n') xml.write('\t\t<name>'+label_+'</name>\n') xml.write('\t\t<pose>Unspecified</pose>\n') xml.write('\t\t<truncated>0</truncated>\n') xml.write('\t\t<difficult>0</difficult>\n') xml.write('\t\t<bndbox>\n') xml.write('\t\t\t<xmin>' + str(xmin) + '</xmin>\n') xml.write('\t\t\t<ymin>' + str(ymin) + '</ymin>\n') xml.write('\t\t\t<xmax>' + str(xmax) + '</xmax>\n') xml.write('\t\t\t<ymax>' + str(ymax) + '</ymax>\n') xml.write('\t\t</bndbox>\n') xml.write('\t</object>\n') print(filename,xmin,ymin,xmax,ymax,labels) xml.write('</annotation>') ``` ## 依比例分train和test ```python= train_percent = 0.8 # 訓練集佔訓練加驗證集比例 # xmlfilepath = 'VOCdevkit/VOC2007/Annotations' #標註檔路徑 filepath = 'VOCdevkit/VOC2007/All_Images' total_file = os.listdir(filepath) total_file.sort() num=len(total_file) list=range(num) tr=int(num*train_percent) # train 資料數 tt=int(num-tr) # test 資料數 train= random.sample(list,tr) train = numpy.sort(train) # test=random.sample(train,tt) print('total_file : %d train : %d test : %d'%(len(total_file), len(train), len(total_file) - len(train))) ftrain = open('VOCdevkit/VOC2007/ImageSets/Main/train.txt', 'w') #指定訓練資料集清單 ftest = open('VOCdevkit/VOC2007/ImageSets/Main/test.txt', 'w') #指定測試資料集清單 for i in list: name=total_file[i][:-4]+'\n' if i in train: ftrain.write(name) else: ftest.write(name) ftrain.close() ftest.close() ``` ## 轉成yolo要的輸入格式 ```python= sets=[('2020', 'train'), ('2020', 'test')] classes = ["raccoon","kangaroo"] def convert_annotation(image_id, list_file): in_file = open('VOCdevkit/VOC2007/All_Annotations/%s.xml'%(image_id)) tree=ET.parse(in_file) root = tree.getroot() 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: continue cls_id = classes.index(cls) xmlbox = obj.find('bndbox') b = (int(xmlbox.find('xmin').text), int(xmlbox.find('ymin').text), int(xmlbox.find('xmax').text), int(xmlbox.find('ymax').text)) list_file.write(" " + ",".join([str(a) for a in b]) + ',' + str(cls_id)) wd = "." for year, image_set in sets: image_ids = open('VOCdevkit/VOC2007/ImageSets/Main/%s.txt'%(image_set)).read().strip().split() annotation_path = '%s_%s.txt'%(year, image_set) list_file = open(annotation_path, 'w') print("save annotation at %s" % annotation_path) for image_id in image_ids: list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg'%(wd, year, image_id)) convert_annotation(image_id, list_file) list_file.write('\n') list_file.close() ``` 下方是程式碼的來源網站,但我有做小改,慢慢了解,慢慢看吧。 辛苦了~~ (強化圖片的程式碼)Reference : https://medium.com/@a.karazhay/guide-augment-images-and-multiple-bounding-boxes-for-deep-learning-in-4-steps-with-the-notebook-9b263e414dac (csv2xml的程式碼)Reference : https://github.com/spytensor/prepare_detection_dataset/blob/master/csv2voc.py