深度學習一行一行敲faster rcnn-keras版(3.3,calc_rpn函數)

對源碼進行逐句解析,盡量說的很細緻。

歡迎各位看官捧場!

源碼地址:keras版本faster rcnn

想了解這篇文章的前後內容出門左拐:faster rcnn代碼理解-keras(目錄)

視頻目錄:深度學習一行一行敲faster rcnn-keras版(視頻目錄)

這章是關於--data_generators.py的calc_rpn函數

該函數的作用是得到每一個anchor的性質

本章函數流程

函數輸入:

def calc_rpn(C, img_data, width, height, resized_width, resized_height, img_length_calc_function):n

  1. 訓練信息類
  2. 圖片信息
  3. 圖片寬度
  4. 圖片高度(重新計算bboxes要用)
  5. 規整化後圖片寬度
  6. 規整化後圖片高度
  7. 計算特徵圖大小函數

函數輸出:

return np.copy(y_rpn_cls), np.copy(y_rpn_regr)n

  1. 是否包含類
  2. 相應的回歸梯度

【註:其只會返回num_regions(這裡設置為256)個有效的正負樣本 】

函數流程:


-------------------------------①------------------------------

(output_width, output_height) = img_length_calc_function(resized_width, resized_height)n

計算特徵圖的大小

for bbox_num, bbox in enumerate(img_data[bboxes]):nttgta[bbox_num, 0] = bbox[x1] * (resized_width / float(width))nttgta[bbox_num, 1] = bbox[x2] * (resized_width / float(width))nttgta[bbox_num, 2] = bbox[y1] * (resized_height / float(height))nttgta[bbox_num, 3] = bbox[y2] * (resized_height / float(height))n

將最短邊規整到指定的長度後,相應的邊框長度也需要發生變化。注意gta的存儲形式是(x1,x2,y1,y2)而不是(x1,y1,x2,y2)

for anchor_size_idx in range(len(anchor_sizes)):ntfor anchor_ratio_idx in range(len(anchor_ratios)):nttanchor_x = anchor_sizes[anchor_size_idx] * anchor_ratios[anchor_ratio_idx][0]nttanchor_y = anchor_sizes[anchor_size_idx] * anchor_ratios[anchor_ratio_idx][1]n

遍歷所有可能的寬組合

for ix in range(output_width):ntx1_anc = downscale * (ix + 0.5) - anchor_x / 2ntx2_anc = downscale * (ix + 0.5) + anchor_x / 2nntif x1_anc < 0 or x2_anc > resized_width:nttcontinuenntfor jy in range(output_height):ntty1_anc = downscale * (jy + 0.5) - anchor_y / 2ntty2_anc = downscale * (jy + 0.5) + anchor_y / 2nnttif y1_anc < 0 or y2_anc > resized_height:ntttcontinuen

遍歷一種預選框組合下,由錨點衍生出的所有預選框

  1. output_width,output_height:特徵圖的寬度與高度
  2. downscale:將特徵圖坐標映射到原圖的比例
  3. if語句是將超出圖片的框刪去

【註:現在我們確定了一個預選框組合有確定了中心點那就是唯一確定一個框了,接下來就是來確定這個寬的性質了:是否包含物體、如包含物體其回歸梯度是多少】

要確定以上兩個性質,每一個框都需要遍歷圖中的所有bboxes

for bbox_num in range(num_bboxes):ntcurr_iou = iou([gta[bbox_num, 0], gta[bbox_num, 2], gta[bbox_num, 1], gta[bbox_num, 3]],[x1_anc, y1_anc, x2_anc, y2_anc])n

計算該預選框與bbox的交並比

def iou(a, b):n if a[0] >= a[2] or a[1] >= a[3] or b[0] >= b[2] or b[1] >= b[3]:n return 0nn area_i = intersection(a, b)n area_u = union(a, b, area_i)nn return float(area_i) / float(area_u + 1e-6)n

  1. if語句是要求右下角的點大於左上角的點,屬於邏輯檢查
  2. intersection:計算兩個面積的交
  3. union:計算兩個面積的並
  4. 最後返回交並並比【分母加上1e-6,是為了防止分母為0】

def intersection(ai, bi):n x = max(ai[0], bi[0])n y = max(ai[1], bi[1])n w = min(ai[2], bi[2]) - xn h = min(ai[3], bi[3]) - yn if w < 0 or h < 0:n return 0n return w*hn

該函數的思路是:得到交區域的左上和右下角坐標。如果右下坐標大於左上坐標則求交面積

def union(au, bu, area_intersection):n area_a = (au[2] - au[0]) * (au[3] - au[1])n area_b = (bu[2] - bu[0]) * (bu[3] - bu[1])n area_union = area_a + area_b - area_intersectionn return area_unionn

並的區域就好求的多了:兩個區域的總面積減去交面積就可以了


-------------------------------②------------------------------

if curr_iou > best_iou_for_bbox[bbox_num] or curr_iou > C.rpn_max_overlap:ntcx = (gta[bbox_num, 0] + gta[bbox_num, 1]) / 2.0ntcy = (gta[bbox_num, 2] + gta[bbox_num, 3]) / 2.0ntcxa = (x1_anc + x2_anc) / 2.0ntcya = (y1_anc + y2_anc) / 2.0nnttx = (cx - cxa) / (x2_anc - x1_anc)ntty = (cy - cya) / (y2_anc - y1_anc)nttw = np.log((gta[bbox_num, 1] - gta[bbox_num, 0]) / (x2_anc - x1_anc))ntth = np.log((gta[bbox_num, 3] - gta[bbox_num, 2])) / (y2_anc - y1_anc)n

如果現在的交並比curr_iou大於該bbox最好的交並比或者大於給定的閾值則求下列參數,這些參數是後來要用的即回歸梯度

  1. tx:兩個框中心的寬的距離與預選框寬的比
  2. ty:同tx
  3. tw:bbox的寬與預選框寬的比
  4. th:同理

if img_data[bboxes][bbox_num][class] != bg:nnt# all GT boxes should be mapped to an anchor box, so we keep track of which anchor box was bestntif curr_iou > best_iou_for_bbox[bbox_num]:nttbest_anchor_for_bbox[bbox_num] = [jy, ix, anchor_ratio_idx, anchor_size_idx]nttbest_iou_for_bbox[bbox_num] = curr_iounttbest_x_for_bbox[bbox_num, :] = [x1_anc, x2_anc, y1_anc, y2_anc]nttbest_dx_for_bbox[bbox_num, :] = [tx, ty, tw, th]nntif curr_iou > C.rpn_max_overlap:nttbbox_type = posnttnum_anchors_for_bbox[bbox_num] += 1nttif curr_iou > best_iou_for_loc:ntttbest_iou_for_loc = curr_iountttbest_regr = (tx, ty, tw, th)nntif C.rpn_min_overlap < curr_iou < C.rpn_max_overlap:nnttif bbox_type != pos:ntttbbox_type = neutraln

如果相交的不是背景,那麼進行一系列更新

  1. 關於bbox的相關信息更新
  2. 預選框的相關更新:如果交並比大於閾值這是pos
  3. best_iou_for_loc:其記錄的是有最大交並比為多少和其對應的回歸梯度
  4. num_anchors_for_bbox[bbox_num]:記錄的是bbox擁有的pos預選框的個數
  5. 如果小於最小閾值是neg,在這兩個之間是neutral
  6. 需要注意的是:判斷一個框為neg需要其與所有的bbox的交並比都小於最小的閾值

if bbox_type == neg:n y_is_box_valid[jy, ix, anchor_ratio_idx + n_anchratios * anchor_size_idx] = 1n y_rpn_overlap[jy, ix, anchor_ratio_idx + n_anchratios * anchor_size_idx] = 0nelif bbox_type == neutral:n y_is_box_valid[jy, ix, anchor_ratio_idx + n_anchratios * anchor_size_idx] = 0n y_rpn_overlap[jy, ix, anchor_ratio_idx + n_anchratios * anchor_size_idx] = 0nelse:n y_is_box_valid[jy, ix, anchor_ratio_idx + n_anchratios * anchor_size_idx] = 1n y_rpn_overlap[jy, ix, anchor_ratio_idx + n_anchratios * anchor_size_idx] = 1n start = 4 * (anchor_ratio_idx + n_anchratios * anchor_size_idx)n y_rpn_regr[jy, ix, start:start+4] = best_regrn

當結束對所有的bbox的遍歷時,來確定該預選寬的性質。

  1. y_is_box_valid:該預選框是否可用(nertual就是不可用的)
  2. y_rpn_overlap:該預選框是否包含物體
  3. y_rpn_regr:回歸梯度

for idx in range(num_anchors_for_bbox.shape[0]):ntif num_anchors_for_bbox[idx] == 0:ntt# no box with an IOU greater than zero ...nttif best_anchor_for_bbox[idx, 0] == -1:ntttcontinuentty_is_box_valid[best_anchor_for_bbox[idx,0], best_anchor_for_bbox[idx,1], best_anchor_for_bbox[idx,2] + n_anchratios *best_anchor_for_bbox[idx,3]] = 1ntty_rpn_overlap[best_anchor_for_bbox[idx,0], best_anchor_for_bbox[idx,1], best_anchor_for_bbox[idx,2] + n_anchratios *best_anchor_for_bbox[idx,3]] = 1nttstart = 4 * (best_anchor_for_bbox[idx,2] + n_anchratios * best_anchor_for_bbox[idx,3])ntty_rpn_regr[best_anchor_for_bbox[idx,0], best_anchor_for_bbox[idx,1], start:start+4] = best_dx_for_bbox[idx, :]n

如果有一個bbox沒有pos的預選寬和其對應,這找一個與它交並比最高的anchor的設置為pos

ty_rpn_overlap = np.transpose(y_rpn_overlap, (2, 0, 1))nty_rpn_overlap = np.expand_dims(y_rpn_overlap, axis=0)nnty_is_box_valid = np.transpose(y_is_box_valid, (2, 0, 1))nty_is_box_valid = np.expand_dims(y_is_box_valid, axis=0)nnty_rpn_regr = np.transpose(y_rpn_regr, (2, 0, 1))nty_rpn_regr = np.expand_dims(y_rpn_regr, axis=0)n

將深度變到第一位,給向量增加一個維度


-------------------------------③------------------------------

pos_locs = np.where(np(y_rpn_overlap[0, :, :, :] =.logical_and= 1, y_is_box_valid[0, :, :, :] == 1))nneg_locs = np.where(np.logical_and(y_rpn_overlap[0, :, :, :] == 0, y_is_box_valid[0, :, :, :] == 1))n

  1. np.where:

arr = [True,True,False,False,True]nprint(arr)nprint(np.where(arr))n輸出:n[True, True, False, False, True]n(array([0, 1, 4], dtype=int64),)n

輸出滿足條件的位置,先行後列。默認返回數組中True的位置

2.np.logical_and【numpy.logical_and 官方參考文檔】

>>> x = np.arange(5)n>>> np.logical_and(x>1, x<4)narray([False, False, True, True, False], dtype=bool)n

即對每一個位置返回一個True或者False。只有當都是True時才返回True

3.這就是得到正負預選寬的位置

num_regions = 256nnif len(pos_locs[0]) > num_regions / 2:ntval_locs = random.sample(range(len(pos_locs[0])), len(pos_locs[0]) - num_regions / 2)nty_is_box_valid[0, pos_locs[0][val_locs], pos_locs[1][val_locs], pos_locs[2][val_locs]] = 0ntnum_pos = num_regions / 2nnif len(neg_locs[0]) + num_pos > num_regions:ntval_locs = random.sample(range(len(neg_locs[0])), len(neg_locs[0]) - num_pos)nt y_is_box_valid[0, neg_locs[0][val_locs], neg_locs[1][val_locs], neg_locs[2][val_locs]] = 0n

  1. 從可用的預選框中選擇num_regions
  2. 如果pos的個數大於num_regions / 2,則將多下來的地方置為不可用。如果小於pos不做處理
  3. 接下來將pos與neg總是超過num_regions個的neg預選框置為不可用

【註:這個隨機選取的過程是很值得學習的。首先:在給定的範圍內(樣本個數)隨機選取指定個數的序號;其次:將這些序號對應的位置處置為0】

list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]nslice = random.sample(list, 5) # 從list中隨機獲取5個元素,作為一個片斷返回na = random.choice(list)nprint (slice)nprint(a)nprint (list) # 原有序列並沒有改變n輸出:n[1, 5, 6, 9, 4]n3n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]n

Python random模塊(獲取隨機數)常用方法和使用例子

y_rpn_cls = np.concatenate([y_is_box_valid, y_rpn_overlap], axis=1)ny_rpn_regr = np.concatenate([np.repeat(y_rpn_overlap, 4, axis=1), y_rpn_regr], axis=1)nnreturn np.copy(y_rpn_cls), np.copy(y_rpn_regr)n

y_rpn_cls:是否包含類,其前半段是該anchor是否可用

y_rpn_regr:回歸梯度,前半段包含是否有效

最後當然是附上測試的最終效果圖啦!

歡迎關注公眾號:huangxiaobai880

https://www.zhihu.com/video/919603203111288832
推薦閱讀:

TAG:深度学习DeepLearning | 机器学习 | 源码阅读 |