.jpg)
불필요한 객체에 대한 태그들을 지워주는 작업을 할거에요.
혹시 앞에서 읽으신 글이 기억나시나요? 목표로 하는 객체 이외에 태그들이 데이터에 존재하고 있었다는 사실을!
어떤 이미지에는 저희가 원하는 목표 객체가 존재하지 않을 수도 있었겠죠? 그래서 목표로 하는 객체가 존재하지 않는 이미지들을 데이터셋에서 걷어내주어야 했죠.
제가 전달받은 dataset은 이미지가 약 30만장 이였어요. 하나하나 annotation파일들을 열어서 확인한다(?) 어림도 없죠. 간단한 프로그램들을 작성하였답니다. 순서대로 따라와주세요. 자연스럽게 데이터셋이 정리될 거에요.
#해당 경로에 해당하는 xml 파일들을 읽어온후 obstacle_classes 리스트에 존재하는 tag들이 포함된 xml 파일들을 뽑아서 "trainval.txt"와 "test.txt"로 만들어주는 꿀스크립트
# Made by JinHaSong
import os
from xml.etree.ElementTree import parse
#여기엔 Resize 데이터에서 VOC20XX 까지의 경로를 넣어주면 됨!
dataset_path = '/hdd/TMP_D/Resized_Data_Voc_For_Song/VOCdevkit/VOC2007'
trainval_txt_path = os.path.join(dataset_path, 'ImageSets/Main/trainval.txt')
test_txt_path = os.path.join(dataset_path, 'ImageSets/Main/test.txt')
annotations_dir_path = os.path.join(dataset_path, 'Annotations')
obstacle_classes = ['person', 'bicycle', 'bus', 'car', 'motorcycle_movable', 'movable_signage', 'trunk', 'chair']
num_of_fold = 10
train_ratio = 9
origin_annotations_list = os.listdir(annotations_dir_path)
origin_annotations_len = len(origin_annotations_list)
annotations_list_train = origin_annotations_list[:int(origin_annotations_len/num_of_fold * train_ratio)]
annotations_list_test = origin_annotations_list[int(origin_annotations_len/num_of_fold * train_ratio)+1:]
annotations_lists = [annotations_list_train, annotations_list_test]
trainval_txt = open(trainval_txt_path, 'w')
test_txt = open(test_txt_path, 'w')
txt_list = [trainval_txt, test_txt]
print("INFO: # of Annotations: ", len(origin_annotations_list))
print("INFO: trainval.txt path: ", trainval_txt_path)
annotations_count = 0
for annotations_list, txt in zip (annotations_lists, txt_list) :
for idx, annotation_name in enumerate(annotations_list):
print("\r[{}/{}]".format(idx, len(annotations_list)), end='')
try :
annotation = parse(os.path.join(annotations_dir_path, annotation_name))
objects = annotation.findall("object")
names = [obj.findtext("name") for obj in objects]
is_object = False
if len(objects) == 0 :
print(annotation_name)
for name in names:
for obstacle_class in obstacle_classes :
if name == obstacle_class :
is_object = True
for object in objects:
bbox = object.find('bndbox')
x1 = float(bbox.find('xmin').text) - 1
y1 = float(bbox.find('ymin').text) - 1
x2 = float(bbox.find('xmax').text) - 1
y2 = float(bbox.find('ymax').text) - 1
if x2 == 0 or y2 == 0 :
print(annotation_name)
if is_object == True:
txt.write(annotation_name.replace(".xml", "") + "\n")
annotations_count += 1
else :
print("\t" + annotation_name)
except :
pass
print()
# print(annotation_name)
print()
trainval_txt.close()
test_txt.close()
print("INFO: train annotation count: ", annotations_count)
위 스크립트는 @jinhasong님으로 부터 전달 받은 코드입니다.
위 코드에서 바꾸면 되는 부분은 총 2개에요.
dataset_path = '경로' 그리고 obstacle_classes=[객체 검출 목표 클래스이름들] 이에요.
목표로 하는 객체들만 들어가 있는 이미지 파일 이름들에 대한 텍스트 파일을 만드셨나요?
다시금 VOC 포맷의 폴더구조를 생성해주세요(저장공간이 넉넉한 곳에). 이제 어떤 일을 할 것이냐 하면
이건 또 어떻게 찾아서 어떻게 옮기지..? 라고 할 수 있어요. 스크립트를 만들어놓았어요. 붙여넣고 몇 줄만 바꿔서 실행만 시키시면 됩니다.
#teleporter.py
#src 에 있는 파일들을 읽어와서 dst로 복사해주는 텔레포트 스크립트!(이 땐 jpg랑 xml 파일들 복사 해줄 때 씀)
# Made by BigJoon
import os
from xml.etree.ElementTree import parse
import shutil
#First Read "trainval.txt"
f = open('trainval.txt',mode='rt',encoding='utf-8')
line_num=1
line = f.readline()
while line:
src = '/hdd/TMP_D/Resized_Data_Voc_For_Song/VOCdevkit/VOC2007/Annotations/'+line.rstrip('\n')+'.xml'
#print(src)
dst = '/hdd/Fresh_Data/Class8_A/VOCdevkit/VOC2007/Annotations/'
shutil.copy(src,dst)
line = f.readline()
line_num +=1
f.close()
진행상황을 못 봐서 답답하신 분이 계시다면 Python3 라이브러리 tqdm을 사용하시면 됩니다. 여기 코드에는 추가 안했지만 전 tqdm을 사용하여 진행상황을 눈으로 보는걸 좋아해요. tqdm사용법은 나중에 포스팅 해드릴게요.
간단하게 teleporter.py 사용법을 알려드릴게요.
소스를 보시면 파일을 오픈 하는 부분이 보일 거에요. 거기에 파일의 경로던가 아니면 해당 스크립트를 텍스트 파일이 있는 곳에서 실행시켜 주세요. while 반복문 안에 보면 src와 dst가 보일거에요.
src에는 우리가 Resize(리사이즈) 시켰던 dataset의 주석 파일이 있는 곳의 경로를 넣어주세요. 그리고 뒤에 확장자는 '.xml'인거 확인해주세요.
dst에는 새롭게 VOC구조의 폴더를 만든 경로에서 Annotations폴더로의 경로를 지정해주세요.
이 코드를 실행시키면 "trainval.txt"(저희가 원하는 객체들이 들어있는 파일의 이름들)을 읽으며 해당 파일을 목적지 경로로 복사해줘요. 그리고 python3 teleporter.py 로 실행해주세요.
주석 파일들을 다 옮겼나요? 그렇다면 이제 image파일들도 옮겨줘야죠.
#teleporter.py
#src 에 있는 파일들을 읽어와서 dst로 복사해주는 텔레포트 스크립트!(이 땐 jpg랑 xml 파일들 복사 해줄 때 씀)
#Made by BigJoon
import os
from xml.etree.ElementTree import parse
import shutil
#First Read "trainval.txt"
f = open('trainval.txt',mode='rt',encoding='utf-8')
line_num=1
line = f.readline()
while line:
src = '/hdd/TMP_D/Resized_Data_Voc_For_Song/VOCdevkit/VOC2007/JPEGImages/'+line.rstrip('\n')+'.jpg'
#print(src)
dst = '/hdd/Fresh_Data/Class8_A/VOCdevkit/VOC2007/JPEGImages/'
shutil.copy(src,dst)
line = f.readline()
line_num +=1
f.close()
src 와 dst에 들어갈 경로 그리고 확장자 이름까지 바꿔주시고 실행해주세요.
그러면 여러분은 이제 Resize된 이미지, Annotation 그리고 목표로 하는 객체가 포함된 dataset을 갖게 된 것이랍니다. 하지만 아직도 dataset에는 불필요한 태그가 포함되어 있어요. 거의 다 왔어요! 힘내요!! 으쌰으쌰~
그 전에 제가 갖고 있는 annotation 파일 중 하나를 보여줄게요.
<Annotation>
<filename>XX_XXX_153153.jpg</filename>
<size>
<width>300</width>
<height>300</height>
<depth>3</depth>
</size>
<object>
<name>barricade</name>
<difficult>0</difficult>
<bndbox>
<xmin>0</xmin>
<ymin>186</ymin>
<xmax>85</xmax>
<ymax>256</ymax>
</bndbox>
</object>
<object>
<name>car</name>
<difficult>0</difficult>
<bndbox>
<xmin>235.0</xmin>
<ymin>111.0</ymin>
<xmax>245.0</xmax>
<ymax>126.0</ymax>
</bndbox>
</object>
<object>
<name>car</name>
<difficult>0</difficult>
<bndbox>
<xmin>259.0</xmin>
<ymin>126.0</ymin>
<xmax>274.0</xmax>
<ymax>146.0</ymax>
</bndbox>
</object>
</Annotation>
제가 파인튜닝을 목표로 하는 객체들은 {person, bicycle, bus, car, carrier, movable_signage, truck, bollard, chair, potted_plant, table, tree_trunk, pole, fire_hydrant} 랍니다. 그런데 중간에 있는 'barricade'는 저희가 원하는 object가 아니거든요. 이제 저희의 잘 정리된 dataset에서 저런 object들을 모조리 없애버리자구요!
"""
python3 erase_outsider_tag.py
This program erase other object tag except {person, bicycle, bus, car, carrier, movable_signage, truck, bollard, chair, potted_plant, table, tree_trunk, pole, fire_hydrant}
Made By Bigjoon
"""
import os
import xml.etree.ElementTree as ET
#XML annotation 파일 들이 들어있는 경로를 "path"에 넣으면 됨.
path = "/hdd/Fresh_Data/Class8_A/VOCdevkit/VOC2007/Annotations/"
for root, dirs, files in os.walk(path):
for fname in files:
#full_fname = os.path.join(root,fname)
tree = ET.parse(path+fname)
roote = tree.getroot()
for test in roote.iter('Annotation'):
for stuff in test.findall('object'):
if stuff.find("name").text != 'person' and stuff.find("name").text != 'bicycle' and stuff.find("name").text!='bus' and stuff.find("name").text!='car' and stuff.find("name").text!= 'motorcycle' and stuff.find("name").text !='movable_signage' and stuff.find("name").text != 'truck' and stuff.find("name").text != 'chair':
test.remove(stuff)
tree.write(path+fname)
이 스크립트의 사용법을 알려드릴게요. path에 annotation파일들이 위치한 경로를 넣어주세요.(끝에 '/'넣는 부분도 빼놓으면 안됩니다) 중간에 보시면 이중 반복문 구조가 보일거에요. if문 안에 형식에 맞게 남겨놓고 싶은, 즉 저희가 목표로 하는 객체 이름만 쓰면 된답니다. 그리고 python3 erase_outsider_tag.py로 실행해주시면 VOC 형식 데이터 정제하기가 모두 끝이 나게 된답니다.
도움이 조금이라도 되었길 바랄게요.^^
너무 유용해요 ~~