Caffe学习(五):Caffe py-Faster-RCNN 源码解析(一)

源码:https://github.com/rbgirshick/py-faster-rcnn

首先来看\tools\train_net.py,首先parse_args()读入命令行设置的参数信息,然后读取yml设置的参数信息,如下:

代码段1:

if __name__ == '__main__':
    args = parse_args()

    print('Called with args:')
    print(args)

    if args.cfg_file is not None:
        cfg_from_file(args.cfg_file)  #读取'.\\experiments\\cfgs\\faster_rcnn_end2end.yml'文件中的参数
    if args.set_cfgs is not None:
        cfg_from_list(args.set_cfgs)

    cfg.GPU_ID = args.gpu_id  #设置gpu id号

    print('Using config:')
    pprint.pprint(cfg)

    if not args.randomize:
        # fix the random seeds (numpy and caffe) for reproducibility
        np.random.seed(cfg.RNG_SEED)
        caffe.set_random_seed(cfg.RNG_SEED)

    # set up caffe
    caffe.set_mode_gpu()
    caffe.set_device(args.gpu_id)

    # imdb_name为voc_2007_trainval,返回数据集,及数据集中的框标记信息(包括框类别,图片路径,宽高等),这里roidb为list,长度10022
    imdb, roidb = combined_roidb(args.imdb_name) 
    print '{:d} roidb entries'.format(len(roidb))

    output_dir = get_output_dir(imdb)
    print 'Output will be saved to `{:s}`'.format(output_dir)

    train_net(args.solver, roidb, output_dir,
              pretrained_model=args.pretrained_model,
              max_iters=args.max_iters)

1. combined_roidb函数解析

代码段1中的重点函数:

imdb, roidb = combined_roidb(args.imdb_name) 

这里args.imdb_name为voc_2007_trainval,这个函数根据这个数据集名称,加载数据集接口给到imdb, 而roidb则为数据集中图片的标注框坐标及类别信息(包括框类别、坐标、图片路径、图片宽高等)。先看看这个函数的输出结果:

进入该函数的实现,也是在train_net.py文件中,如下代码段2:

代码段2:

def combined_roidb(imdb_names):#imdb_names为数据集名称列表,'imdb_names:voc_2007_trainval'
    def get_roidb(imdb_name):#imdb_names:voc_2007_trainval
        imdb = get_imdb(imdb_name) #返回voc2007数据集
        print 'Loaded dataset `{:s}` for training'.format(imdb.name)
        imdb.set_proposal_method(cfg.TRAIN.PROPOSAL_METHOD) #PROPOSAL_METHOD:'gt', 设置调用的roi函数,在pascal_voc中的gt_roidb
        print 'Set proposal method: {:s}'.format(cfg.TRAIN.PROPOSAL_METHOD)
        roidb = get_training_roidb(imdb) #翻转样本(可选),并获取数据集的图片路径、图片宽高、框的类别等,返回这些信息imdb.roidb
        return roidb

    roidbs = [get_roidb(s) for s in imdb_names.split('+')] #获取各个数据集的训练样本的标注框信息
    roidb = roidbs[0]  #获取第一个数据集的标注框信息
    if len(roidbs) > 1:  #如果是多个数据集
        for r in roidbs[1:]:  #将其他数据集的数据添加到roidb末尾
            roidb.extend(r)
        imdb = datasets.imdb.imdb(imdb_names)
    else:
        imdb = get_imdb(imdb_names)
    return imdb, roidb

代码段2的第10行,imdb_names是一个列表,数据集名称用‘+’分割,这里调用get_roidb获取各个数据集的信息。在get_roidb中,调用lib\datasets\factory.py中的get_imdb获取数据集索引,如下代码段3,__sets在lib\datasets\pascal_voc.py中已加载voc2007数据集的信息

代码段3:

def get_imdb(name):
    """Get an imdb (image database) by name."""
    if not __sets.has_key(name):
        raise KeyError('Unknown dataset: {}'.format(name))
    return __sets[name]() #返回已加载的voc2007数据集

代码段2的第5行,imdb.set_proposal_method(cfg.TRAIN.PROPOSAL_METHOD) 设置训练样本标注框的获取方式,这里PROPOSAL_METHOD为‘gt’,调用到lib\datasets\imdb.py以下函数,

代码段4:

def set_proposal_method(self, method):#method:'gt'
    method = eval('self.' + method + '_roidb') #self.gt_roidb,在子类pascal_voc中实现
    self.roidb_handler = method #将self.roidb_handler绑定为method函数接口

method为self.gt_roidb,这里将imdb.py文件中的imdb类本身并没有gt_roidb函数,该函数的实现由pascal_voc.py中的pascal_voc类来实现,而pascal_voc继承了imdb类,gt_roidb的实现如下:

代码段5:

def gt_roidb(self):
    """
    Return the database of ground-truth regions of interest.
    This function loads/saves from/to a cache file to speed up future calls.
    """
    #cache_path:'C:\\zhh\\py-faster-rcnn\\data\\cache', 'voc_2007_trainval'
    cache_file = os.path.join(self.cache_path, self.name + '_gt_roidb.pkl')#'C:\\zhh\\py-faster-rcnn\\data\\cache\\voc_2007_trainval_gt_roidb.pkl'
    if os.path.exists(cache_file):
        with open(cache_file, 'rb') as fid:
             roidb = cPickle.load(fid)
        print '{} gt roidb loaded from {}'.format(self.name, cache_file)
        return roidb

    gt_roidb = [self._load_pascal_annotation(index)
                    for index in self.image_index]
    with open(cache_file, 'wb') as fid:
         cPickle.dump(gt_roidb, fid, cPickle.HIGHEST_PROTOCOL)
    print 'wrote gt roidb to {}'.format(cache_file)

    return gt_roidb

gt_roidb就是将py-faster-rcnn\\data\\cache\\voc_2007_trainval_gt_roidb.pkl中的标注框信息解析出来。

在回来看代码段2中的第7行,  roidb = get_training_roidb(imdb) ,这个函数从voc2007数据集中获取标注信息,roidb为一个List,大小为10022,在get_trainning_roidb函数中,为了扩充数据,对voc2007的5011张训练图片做了翻转处理,得到10022张训练集图片,roidb的信息如下:

再来看get_trainning_roidb的实现,这个函数在\tools\train.py中实现如下:

代码段6

#翻转样本(可选),并获取数据集的图片路径、图片宽高、框的类别等,返回这些信息imdb.roidb
def get_training_roidb(imdb):
    """Returns a roidb (Region of Interest database) for use in training."""
    if cfg.TRAIN.USE_FLIPPED:  #是否翻转样本,这里是True
        print 'Appending horizontally-flipped training examples...'
        imdb.append_flipped_images() #对样本做左右翻转,样本数量翻倍
        print 'done'

    print 'Preparing training data...'
    rdl_roidb.prepare_roidb(imdb)  #获取数据集的图片路径、图片宽高、框的类别等
    print 'done'

    return imdb.roidb

代码段6中的第6行imdb.append_flipped_images() 对训练样本做翻转处理,其实现如下,可以看到其实并没有对图片本身做翻转,而是计算翻转后的标注框的坐标,然后用flipped:true标记是否做翻转处理。

代码段7:

 # 对5011张训练集图片的标注框做左右翻转,需要处理的是标记框中的x坐标要做处理。处理后得到5011x2张训练集图片信息    
def append_flipped_images(self):
    num_images = self.num_images  #5011
    widths = self._get_widths() #widths:list, len5011, voc2007中每张训练图片的宽度
    for i in xrange(num_images):#
        boxes = self.roidb[i]['boxes'].copy()  #调用pascal_voc 中的gt_roidb读取voc2007 pkl文件,self.roidb返回的是一个list,大小为5011, 这里获取第i图片的标记框坐标
         oldx1 = boxes[:, 0].copy() #获取框的左上角 x 坐标,这里有3个boxes, 因此oldx1长度为3,[262, 164, 240]
         oldx2 = boxes[:, 2].copy()#获取框的右下角 x 坐标,这里有3个boxes, 因此oldx1长度为3,[323, 252, 294]
         boxes[:, 0] = widths[i] - oldx2 - 1 #宽度减去右下角 x 坐标,作为框的左上角x 坐标,即将图片左右翻转
          boxes[:, 2] = widths[i] - oldx1 - 1 #宽度减去左上角 x 坐标,作为框的右下角 x 坐标,即将图片左右翻转
         assert (boxes[:, 2] >= boxes[:, 0]).all() #右下角 x 坐标 要比 左上角 x 坐标 值要大
         entry = {'boxes' : boxes,
                     'gt_overlaps' : self.roidb[i]['gt_overlaps'],
                     'gt_classes' : self.roidb[i]['gt_classes'],
                     'flipped' : True}
         self.roidb.append(entry) #将翻转后的图片加入到roidb队列
    self._image_index = self._image_index * 2 #图片名称复制一份

再来看代码段6中的第10行rdl_roidb.prepare_roidb(imdb)  这个函数获取扩充后的数据集的图片路径、图片宽高、框的类别等,其在lib\roi_data_layer\roidb.py中实现如下:

代码段8:

def prepare_roidb(imdb):
    """Enrich the imdb's roidb by adding some derived quantities that
    are useful for training. This function precomputes the maximum
    overlap, taken over ground-truth boxes, between each ROI and
    each ground-truth box. The class with maximum overlap is also
    recorded.
    """
    sizes = [PIL.Image.open(imdb.image_path_at(i)).size
             for i in xrange(imdb.num_images)]#10022(原来5011张,翻转后10022张)张训练图片的宽高
    roidb = imdb.roidb #10022张训练图片的标注框信息
    for i in xrange(len(imdb.image_index)):
        roidb[i]['image'] = imdb.image_path_at(i)  #图片的路径,第一张图为000005.jpg,该图中标注有3个框,都是chair(类别序号为9)
        roidb[i]['width'] = sizes[i][0]  #图片的宽
        roidb[i]['height'] = sizes[i][1] #图片的高
        # need gt_overlaps as a dense array for argmax
        gt_overlaps = roidb[i]['gt_overlaps'].toarray()  #3x21, 有3个框,一行表示1个框,每列表示该框的类别,值为1,表示框的类别
        # max overlap with gt over classes (columns)
        max_overlaps = gt_overlaps.max(axis=1) #获取3x21中行方向(水平方向)中的最大值这里得到[1,1,1]
        # gt class that had the max overlap
        max_classes = gt_overlaps.argmax(axis=1) #获取行方向(水平)中最大值的列序号,这里得到[9,9,9]# 框的类别
        roidb[i]['max_classes'] = max_classes # 框的类别
        roidb[i]['max_overlaps'] = max_overlaps
        # sanity checks
        # max overlap of 0 => class should be zero (background)
        zero_inds = np.where(max_overlaps == 0)[0] #zero_inds空
        assert all(max_classes[zero_inds] == 0)
        # max overlap > 0 => class should not be zero (must be a fg class)
        nonzero_inds = np.where(max_overlaps > 0)[0] #[0,1,2]
        assert all(max_classes[nonzero_inds] != 0)

在prepare_roidb中遍历扩充后的10022张训练集图片,获取图片的路径、宽高,标注框的类别,保存到imdb的roidb中。至此代码段2分析完毕。

2. train_net解析

发布了40 篇原创文章 · 获赞 51 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/zh8706/article/details/95209112