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解析

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章