NMS,Bucketing,ANMS
上一章提到不管是NMS还是Bucketing的方法都无法获得最优的关键点空间分布。前者在窗口内避免了关键点的聚集效应,但是无法保证全局的均匀分布;后者能保证全局大概均匀分布,但是无法保证局部不聚集(当然目前的实现中往往opencv已经做了3x3的NMS,但是这种窗口的大小无法保证真正意义上的不聚集)。更大的问题是这这两种算法都有一些超参数需要手动调整,导致适用的场景有一定的限制。
为解决这一问题,Matthew Brown在论文Multi-Image Matching using Multi-Scale Oriented Patches提出了自适应非极大值抑制算法(Adaptive Non-Maximum Suppression,ANMS),实现了特征点在局部上的稀疏分布和全局上的均匀分布。
自适应非极大值抑制算法
自适应非极大值抑制算法基本思想是评估所有候选点的极大区域,并进行排序。
具体来说,就是先选取很多的评分较高的候选点,组成集合S。对S中的每个点 x i x_i xi,寻找它的响应能作为区域最大值的区域半径 r i r_i ri,即
r i = m i n ∣ ∣ x i − x j ∣ ∣ , s . t . r e s p o n s e ( x i ) < r e s p o n s e ( x j ) , x j ∈ S r_i =min||x_i-x_j||,s.t.response(x_i)<response(x_j),x_j \in S ri=min∣∣xi−xj∣∣,s.t.response(xi)<response(xj),xj∈S
将找到的 r i r_i ri降序排列。选取前n个元素对应的点,即为自适应非极大值滤波后得到的关键点。
算法效果如图:
过滤前
过滤后
有兴趣的话可以与上一章中的NMS和Bucketing的结果进行对比。
Why NOT ANMS
不同于传统非极大值抑制算法,ANMS算法不用选取阈值。然而带来的问题是它需要计算所有候选点之间的距离,所以会带来很大的时间复杂度,过滤10000个点得到1000个点,就需要耗费接近1s的时间。因此我们在在线的工作中很难看到ANMS得到应用。
实现代码如下
double computeR(Point2i x1, Point2i x2)
{
return norm(x1 - x2);
}
template < typename T>
vector< size_t> sort_indexes(const vector< T> & v) {
// initialize original index locations
vector< size_t> idx(v.size());
for (size_t i = 0; i != idx.size(); ++i) idx[i] = i;
// sort indexes based on comparing values in v
sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2) {return v[i1] > v[i2]; });
return idx;
}
vector<KeyPoint> ANMS(const std::vector<KeyPoint>& kpts,int num = 500)
{
int sz = kpts.size();
double maxmum = 0;
vector<double> roblocalmax(kpts.size());
vector<double> raduis(kpts.size(), INFINITY);
for (size_t i = 0; i < sz; i++)
{
auto rp = kpts[i].response;
if (rp > maxmum)
maxmum = rp;
roblocalmax[i] = rp*0.9;
}
auto max_response = maxmum*0.9;
for (size_t i = 0; i < sz; i++)
{
double rep = kpts[i].response;
Point2i p = kpts[i].pt;
auto& rd = raduis[i];
if (rep>max_response)
{
rd = INFINITY;
}
else
{
for (size_t j = 0; j < sz; j++)
{
if (roblocalmax[j] > rep)
{
auto d = computeR(kpts[j].pt, p);
if (rd > d)
rd = d;
}
}
}
}
auto sorted = sort_indexes(raduis);
vector<KeyPoint> rpts;
for (size_t i = 0; i < num; i++)
{
rpts.push_back(kpts[sorted[i]]);
}
return std::move(rpts);
}