简介
ResNet
+ FPN
(将不同维度的特征整合在一起,提高信息的丰富度) + SubNet
(class/regression:对anchor做平移和伸缩变换) + focal-loss
- 仔细观察
Retinanet
,我们不难发现,Retinanet
是一个one-stage
的网络,那么为什么它的精度竟然超过了two-stage呢?这就是focal loss的效果。
DataLoader, DataSet, Sampler
- DataLoader:sampler作用是生成一系列的index,batch_sampler则是将sampler生成的indices打包分组,得到一个又一个batch的index
- Pytorch中实现的Sampler:
SequentialSampler / RandomSampler / WeightedSampler / SubsetRandomSampler
- 自定义Sampler和BatchSampler:
__iter__(self)
函数,不过要注意的是该函数的返回值需要是可迭代的
- Dataset:想对
__getitem__
方法进行调试,你可以写一个for
循环遍历dataset
来进行调试了,而不用构建dataloader
等,建议学会使用ipdb
这个库
self.collate_fn
:作用就是将一个batch的数据进行合并操作。默认的collate_fn
是将img和label
分别合并成imgs和labels
,所以如果你的__getitem__
方法只是返回 img, label
那么你可以使用默认的collate_fn方法,但是如果你每次读取的数据有img, box, label
等等,那么你就需要自定义collate_fn
来将对应的数据合并成一个batch数据,这样方便后续的训练步骤
制作类似VOC格式的个人数据集
介绍VOC数据集
- JPEGImages文件夹
- Annatations文件夹
- 文件夹存放的是xml格式的标签文件,每个xml文件都对应于JPEGImages文件夹的一张图片
- ImageSets文件夹
- Action存放的是人的动作,暂时不用
- Layout存放的人体部位的数据,暂时不用
- Main存放的是图像物体识别的数据,Main里面有test.txt , train.txt, val.txt ,trainval.txt.这四个文件我们后面会生成
- Segmentation存放的是可用于分割的数据
- 其他的文件夹不解释了,其他任务用的(如语义风格个个人分割)
def make_voc_dir():
os.makedirs('VOC2007/Annotations')
os.makedirs('VOC2007/ImageSets')
os.makedirs('VOC2007/ImageSets/Main')
os.makedirs('VOC2007/ImageSets/Layout')
os.makedirs('VOC2007/ImageSets/Segmentation')
os.makedirs('VOC2007/JPEGImages')
os.makedirs('VOC2007/SegmentationClass')
os.makedirs('VOC2007/SegmentationObject')
if __name__ == '__main__':
make_voc_dir()
第一步:搞定JPEGSImages文件夹(改名
)
- 把你的图片放到JPEGSImages里面,在VOC2007里面,人家的图片文件名都是000001.jpg类似这样的,我们也统一格式,把我们的图片名字重命名成这样的
import os
class BatchRename():
def __init__(self):
self.path = 'C:/Users/leeha/Desktop/VOC/JPEGImages/'
def rename(self):
filelist = os.listdir(self.path)
total_num = len(filelist)
i = 0
for item in filelist:
if item.endswith('.jpg'):
src = os.path.join(os.path.abspath(self.path), item)
dst = os.path.join(os.path.abspath(self.path), 'LJ'+str(i+1).rjust(9,'0')+ '.jpg')
try:
os.rename(src, dst)
print('converting {} to {} ...'.format(src, dst))
i = i + 1
except:
continue
print('total {} to rename & converted {} jpgs'.format(total_num, i))
if __name__ == '__main__':
demo = BatchRename()
demo.rename()
第二步:搞定Annatations文件夹
第三步:搞定ImageSet/Main/*.txt文件
get_mian_txt.py
- 数据集划分:test / train / val
- 如下代码要求:0.7 train,0.3x0.9 test,0.3x0.1 val
import os
import random
from tqdm import tqdm
def _main():
trainval_percent = 0.3
train_percent = 0.9
xmlfilepath = 'Annotations'
total_xml = os.listdir(xmlfilepath)
num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)
ftrainval = open('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/val.txt', 'w')
for i in tqdm(list):
name = total_xml[i][:-4] + '\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftest.write(name)
else:
fval.write(name)
else:
ftrain.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
if __name__ == '__main__':
_main()
第四步:生成相关CSV文件
get_anno_txt.py
- 生成的csv文件格式是:path / x1 / y2 / x2/ y2;如果一个图像上有多个目标,会分成多行!
├── csv
│ ├── Annotations
│ │ ├── 0095.xml
│ │ ├── 0096.xml
│ │ ├── 0312.xml
│ │ ├── 0313.xml
│ │ ├── 0588.xml
│ │ ├── 0589.xml
│ │ ├── 0590.xml
│ │ ├── 0800.xml
│ │ ├── 0801.xml
│ │ ├── 0802.xml
│ │ ├── 0803.xml
│ │ ├── 0804.xml
│ │ └── 0805.xml
│ ├── test_anno.csv
│ ├── test_cls.csv
│ ├── train_anno.csv
│ ├── train_cls.csv
│ ├── val_anno.csv
│ ├── val_cls.csv
│ ├── ImageSets
│ │ └── Main
│ │ ├── test.txt
│ │ ├── train.txt
│ │ ├── trainval.txt
│ │ └── val.txt
│ ├── JPEGImages
│ │ ├── 0095.png
│ │ ├── 0096.png
│ │ ├── 0312.png
│ │ ├── 0313.png
│ │ ├── 0588.png
│ │ ├── 0589.png
│ │ ├── 0590.png
│ │ ├── 0800.png
│ │ ├── 0801.png
│ │ ├── 0802.png
│ │ ├── 0803.png
│ │ ├── 0804.png
│ │ └── 0805.png
│ └── pascal2csv.py
import csv
import os
import glob
import sys
sets = [('train'),('test'),('val')]
path = '/home/idip/lijian/pytorch/Dataset/VOC2007/JPEGImages'
class PascalVOC2CSV(object):
def __init__(self,xml=[], ann_path='./annotations.csv',classes_path='./classes.csv', pre='./Annotations/'):
'''
:param xml: image list
:param ann_path: ann_path
:param classes_path: classes_path
'''
self.pre = pre
self.xml = xml
self.ann_path = ann_path
self.classes_path=classes_path
self.label=[]
self.annotations=[]
self.data_transfer()
self.write_file()
def data_transfer(self):
for num, xml_file in enumerate(self.xml):
xml_file = self.pre + xml_file + '.xml'
try:
sys.stdout.write('\r>> Converting image %d/%d' % (
num + 1, len(self.xml)))
sys.stdout.flush()
with open(xml_file, 'r') as fp:
for p in fp:
if '<filename>' in p:
self.filen_ame = p.split('>')[1].split('<')[0]
if '<object>' in p:
d = [next(fp).split('>')[1].split('<')[0] for _ in range(9)]
self.supercategory = d[0]
if self.supercategory not in self.label:
self.label.append(self.supercategory)
x1 = int(d[-4]);
y1 = int(d[-3]);
x2 = int(d[-2]);
y2 = int(d[-1])
self.annotations.append([os.path.join(path, self.filen_ame),
x1, y1, x2, y2, self.supercategory])
except:
continue
sys.stdout.write('\n')
sys.stdout.flush()
def write_file(self,):
with open(self.ann_path, 'w', newline='') as fp:
csv_writer = csv.writer(fp, dialect='excel')
csv_writer.writerows(self.annotations)
class_name=sorted(self.label)
class_=[]
for num,name in enumerate(class_name):
class_.append([name,num])
with open(self.classes_path, 'w', newline='') as fp:
csv_writer = csv.writer(fp, dialect='excel')
csv_writer.writerows(class_)
if __name__ == '__main__':
for txt_name in sets:
image_ids = open('./ImageSets/Main/%s.txt'%(txt_name)).read().strip().split()
PascalVOC2CSV(image_ids, txt_name + '_anno.csv', txt_name + '_cls.csv')
代码
- Anchors:处理先验框之类的,34567, image_shapes, generate_anchors
- data_loader: CSV_Dataset, Collater, AspectRatioSampler, Resizer, Normalizer
- others: generator_predict_boxes, Adjust_boxes, _compute_ap, compute_overlap, nms
- FPN: FeaturePyramidNet
- resnet
- SubNet
- losses
- Build_Network