代碼位址
https://github.com/xiaoxu1025/r-fcn
對于看了論文一知半解的朋友可以看看我的RCNN系列的實作。
CSDN連結位址:
https://blog.csdn.net/xiaoxu1025/article/details/104134569 RCNN系列之-RCNN keras實作
https://blog.csdn.net/xiaoxu1025/article/details/104127684 RCNN系列之-Faster-RCNN keras實作
https://blog.csdn.net/xiaoxu1025/article/details/104101234 RCNN系列之-Fast-RCNN keras實作
github連結位址:
https://github.com/xiaoxu1025/rcnn-keras
https://github.com/xiaoxu1025/fast-rcnn-keras
https://github.com/xiaoxu1025/faster-rcnn-keras
實作很簡單也很好了解,隻是作為一個學習記錄。有興趣的朋友可用下載下傳下來看看,比看網上各種文章好懂多了。rcnn系列keras實作基本上和源碼實作方式差不多,也不會誤導同學們。如果有幫助到你們,希望給我點個👍哦!!!
接下來 分析下 R-FCN 關于R-FCN的文章網上有很多,我這裡隻簡單講解下
R-FCN其實是為了給faster-rcnn加速的,因為faster-rcnn 在roi層後的全連接配接對所有proposal不共享,比如一張圖檔最後生成300個proposal 每個proposal都需要走一遍後面的全連接配接結構。這其實是很耗時的。所有R-FCN作者提出把roi後的操作提前。但是簡單提前不行,所有作者弄出來了個 Position-sensitive score map
其中r-fcn難點在于roipooling 因為它和faster-rcnn的roipooling操作有點不同。
下面我就具體分析一下(這是我用keras實作的一個roipooling層):
import tensorflow as tf
from tensorflow.keras.layers import Layer
"""
該層是用來對r-fcn進行roipooling
和faster-rcnn roipooling實作不同
"""
class RRoiPooling(Layer):
def __init__(self, im_dims, last_dim, k=3, **kwargs):
# k 是超參數 對 position-sensitive score maps or position-sensitive regression
# 進行roi後feature maps大小
super(RRoiPooling, self).__init__(**kwargs)
self._im_dims = im_dims
self._k = k
# 因為cls 和 reg 都是用的這一層實作 是以這裡将最後一次元穿進來
# cls: C + 1 reg : 4
self._last_dim = last_dim
def call(self, inputs, **kwargs):
"""
:param inputs: inputs shape (None, H, W, K * K * (C + 1)) or (None, H, W, K * K * 4)
第一個是 position-sensitive score maps 第二個是 position-sensitive regression maps
:param rois: (N, 5) (batch_id, x1, y1, x2, y2)
:param kwargs:
:return:
"""
feature_maps, rois = inputs[0], inputs[1]
# reshape 成rank: 2
rois = tf.reshape(rois, (-1, 5))
# 進行rois标準化
k = self._k
feature_split = tf.split(feature_maps, num_or_size_splits=k * k, axis=-1)
# y1, x1, y2, x2
boxes, batch_ids = self._normalize_boxes(rois)
# 将boxes分成k * k個bin 每個bin大小為bin_w * bin_h
bin_w = (boxes[..., 3] - boxes[..., 1]) / k
bin_h = boxes[..., 2] - boxes[..., 0] / k
sensitive_boxes = []
for ih in range(k):
for iw in range(k):
box_coordinates = [boxes[..., 0] + ih * bin_h,
boxes[..., 1] + iw * bin_w,
boxes[..., 0] + (ih + 1) * bin_h,
boxes[..., 1] + (iw + 1) * bin_h]
sensitive_boxes.append(tf.stack(box_coordinates, axis=-1))
features = []
for (feature, box) in zip(feature_split, sensitive_boxes):
# crop對于區域後resize 成 (2k, 2k) 這個大小可以調整 不要太離譜就行
pooled_features = tf.image.crop_and_resize(feature, box, tf.cast(batch_ids, dtype=tf.int32), [k * 2, k * 2])
features.append(pooled_features)
# [(N, 6, 6, self._last_dim), (N, 6, 6, self._last_dim), (N, 6, 6, self._last_dim), ...] 總共9個
# N 為 len(rois) == len(boxes)
# 然後對list中的tesor各個位置上的值相加并求平均
sensitive_features = tf.add_n(features) / len(features)
# (N, self._last_dim)
output = tf.reduce_mean(sensitive_features, axis=[1, 2])
output = tf.expand_dims(output, axis=0)
return output
def _normalize_boxes(self, rois):
"""
對rois 進行normalize 使其滿足tf.image.crop_and_resize方法參數格式
:param rois: (None, 5) batch_id, x1, y1, x2, y2
:return:
"""
im_dims = self._im_dims
normalization = tf.cast(tf.stack([im_dims[1], im_dims[0], im_dims[1], im_dims[0]], axis=0),
dtype=tf.float32)
batch_ids = rois[..., 0]
boxes = rois[..., 1:]
boxes = tf.div(boxes, normalization)
# tf.stop_gradient
# y1, x1, y2, x2
boxes = tf.stack([boxes[..., 1], boxes[..., 0], boxes[..., 3], boxes[..., 2]], axis=-1)
return boxes, batch_ids
計算公式如下:

講一下我個人對這個公式的了解
對于 sensitive position score map 的次元是[H, W, K * K * (C + 1)]
通道次元可以分成 K * K 組 每組(C + 1)維的向量,同時我們還會對我們要進行roipooling的區域(h, w)進行劃分,将其分成 k * k 個bin
每個bin的大小 h / k, w/k 每個bin對應通道中一組C + 1次元向量 順序則是 從上到下 從左到又 就像下面表格中的索引順序。具體實作細節可以參考下我上面的代碼。
1 | 2 | |
3 | 4 | 5 |
6 | 7 | 8 |
對于regresion map 就和score map 是一樣的操作 這裡就不重複了
其他的功能實作,請參考代碼。我就不全部講解了
如果有幫助到大家,請給我點個贊~
與君共勉!!!