Anchor 的两种编程实现

  • aspect ratios:高宽比率

假设 window 的尺寸为:\((w, h)\),锚框的尺寸为:\((w_1, h_1)\),则有:

\[\begin{cases}
\frac{w_1h_1}{wh} = s^2\\
\frac{h_1}{w_1} = \frac{h}{w} r
\end{cases}
\]

可以化简为:

\[\begin{cases}
w_s = \frac{w_1}{s} = \frac{w}{\sqrt{r}} \\
h_s = \frac{h_1}{s} = h \sqrt{r}
\end{cases}
\]

我们可以有两种编程实现方式:

1 \(w=h\)

\[\begin{cases}
w_s = \frac{w_1}{s} = \frac{w}{\sqrt{r}} = \text{round}(\sqrt{\frac{wh}{r}})\\
h_s = \frac{h_1}{s} = h \sqrt{r} = \sqrt{whr} = \text{round}(w_s r)
\end{cases}
\]

编程实现:

import numpy as np

class AnchorBase:
def __init__(self, base_size, scales, ratios):
self.scales = np.array(scales) #
self.ratios = np.array(ratios) #
self.num_anchors = len(self.ratios) * len(self.scales) # 锚框的个数
self.base_size = base_size # 滑动窗口的大小
if isinstance(base_size, int):
self._w, self._h = [base_size]*2
elif len(base_size) == 2:
self._w, self._h = base_size
elif len(base_size) == 1:
self._w, self._h = base_size*2 self._anchor = np.array([1, 1, self._w, self._h]) - 1 @property
def anchor(self):
return self._anchor @anchor.setter
def anchor(self, new_anchor):
self._anchor = new_anchor @property
def w(self):
'''
锚框的宽度
'''
return self.anchor[2] - self.anchor[0] + 1 @property
def h(self):
'''
锚框的高度
'''
return self.anchor[3] - self.anchor[1] + 1 @property
def size(self):
'''
锚框的面积
'''
return self.w * self.h @property
def _whctrs(self):
"""
Return x center, and y center for an anchor (window). 锚框的中心坐标
"""
x_ctr = self.anchor[0] + 0.5 * (self.w - 1)
y_ctr = self.anchor[1] + 0.5 * (self.h - 1)
return np.array([x_ctr, y_ctr]) @staticmethod
def _coordinate(aspect, ctr):
'''
依据宽高组合计算锚框的坐标
'''
k = (aspect - 1) / 2
return np.concatenate([ctr - k, ctr + k], axis=1) class AnchorRCNN(AnchorBase):
def __init__(self, base_size, scales, ratios):
super().__init__(base_size, scales, ratios)
self.anchors = self.gen_anchors() @property
def ratio_aspects(self):
'''
依据 ratios 获取锚框的所有宽高组合
'''
size_ratios = self.size / self.ratios
ws = np.round(np.sqrt(size_ratios))
hs = np.round(ws * self.ratios)
return np.stack([ws, hs], axis=1) @property
def ratio_anchors(self):
return self._coordinate(self.ratio_aspects, self._whctrs) @property
def scale_aspects(self):
'''
依据 scales 获取锚框的所有宽高组合
'''
ws = self.w * self.scales
hs = self.h * self.scales
return np.stack([ws, hs], axis=1) @property
def scale_anchors(self):
return self._coordinate(self.scale_aspects, self._whctrs) def gen_anchors(self):
'''
获取最终的 base_anchors
'''
anchors = []
for anchor in self.ratio_anchors:
self.anchor = anchor
anchors.append(self.scale_anchors)
return np.concatenate(anchors)
scales = [8, 16, 32]  # 尺度,面积比
ratios = [0.5, 1, 2] # window(滑动窗口) 与锚框的面积的比率(aspect ratios)
base_size = 16 # 滑动窗口的大小 self = AnchorRCNN(base_size, scales, ratios) self.anchors
array([[ -84.,  -40.,   99.,   55.],
[-176., -88., 191., 103.],
[-360., -184., 375., 199.],
[ -56., -56., 71., 71.],
[-120., -120., 135., 135.],
[-248., -248., 263., 263.],
[ -36., -80., 51., 95.],
[ -80., -168., 95., 183.],
[-168., -344., 183., 359.]])
self.ratio_anchors
array([[-3. ,  2.5, 18. , 12.5],
[ 0. , 0. , 15. , 15. ],
[ 2.5, -3. , 12.5, 18. ]])

2

\[\begin{cases}
\frac{w_1}{w} = \frac{s}{\sqrt{r}} = \text{round}(\frac{s}{\sqrt{r}})\\
\frac{h_1}{h} = s \sqrt{r} = \text{round}(\frac{w_1}{w} r)
\end{cases}
\]

\[\begin{cases}
S = [s_1, s_1, \cdots, s_m]\\
R = [r_1, r_2, \cdots, r_n]
\end{cases}
\]

则有(下面的运算均是元素级别的元素):

\[\begin{cases}
W = (\frac{s_i}{\sqrt{r_j}}) = \frac{S}{\sqrt{R}}\\
H = (s_i \sqrt{r_j}) = W \cdot R
\end{cases}
\]
class Anchor(AnchorBase):
def __init__(self, base_size, scales, ratios):
super().__init__(base_size, scales, ratios) @property
def W(self):
'''
计算 w_1/ w
'''
W = self.scales[:, None] / np.sqrt(self.ratios)
return np.round(W) @property
def H(self):
'''
计算 h_1/ h
'''
H = self.W * self.ratios
return np.round(H) @property
def aspect(self):
'''
所有的宽高组合
'''
return np.stack([self.W.flatten(), self.H.flatten()], axis=1) @property
def base_anchors(self):
return self._coordinate(self.aspect, self._whctrs) @property
def anchors(self):
'''
获取最终的 base_anchors
'''
return self.base_anchors * np.array([self.w, self.h]*2)
scales = [8, 16, 32]  # 尺度,面积比
ratios = [0.5, 1, 2] # window(滑动窗口) 与锚框的面积的比率(aspect ratios)
base_size = [16, 8]
self = Anchor(base_size, scales, ratios) self.anchors
array([[  40.,    8.,  200.,   48.],
[ 64., 0., 176., 56.],
[ 80., -16., 160., 72.],
[ -56., -16., 296., 72.],
[ 0., -32., 240., 88.],
[ 40., -56., 200., 112.],
[-232., -56., 472., 112.],
[-128., -96., 368., 152.],
[ -56., -152., 296., 208.]])

上一篇:关于fastclick.js


下一篇:Android 6.0 执行时权限处理全然解析