博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《Online Visual Place Recognition via Saliency Re-identification》论文阅读和实验
阅读量:2051 次
发布时间:2019-04-28

本文共 8345 字,大约阅读时间需要 27 分钟。

《Online Visual Place Recognition via Saliency Re-identification》论文阅读和实验

摘要

        作者认为现存的针对地点识别(visual place recognition)的一般方法——特征提取和匹配,均存在计算量较大的问题。人类在地点识别过程中,往往只会记住突出的特征(salient features)。受这个事实的启发,作者提出在频域上的突出特征检测和重定位方法。

相关工作

  • 基于特征的方法

        建立离线视觉词袋,如在FABMAP中使用SURF特征建立词袋。DBoW2使用了类似的方法建立词袋,但使用了ORB描述子。

        建立在线词袋的,如HBST,虽然速度上有较大提升,但内存消耗很大。

  • 基于深度学习的方法
            比较有名的是受传统图像检索方法(VLAD)的启发,有人提出NetVLAD。这是一种可学习的端到端的地点识别方法。然而深度学习方法需要承受很大的计算负担,难以适应实时系统。

原理

        作者开源了代码,因此我感觉原理结合代码会更好理解,而且作者的代码也比较简洁和通俗易懂,不会占用过多篇幅。

1. 突出特征检测

        作者在突出特征检测上使用的是log-spectral residual的方法,这个方法是提出的方法。对于一个输入图像 I I I,假定 ( ⋅ ) ^ \hat{(\cdot)} ()^标记为傅里叶变换。 A ( I ^ ) , P ( I ^ ) A(\hat{I}), P(\hat{I}) A(I^),P(I^)分别表示这个图像的幅值和相位。那么定义log-spectral residual为:

L ( I ^ ) = l o g ( A ( I ^ ) ) R ( I ^ ) = e x p ( L ( I ^ ) − f a v e ∗ L ( I ^ ) ) \begin{aligned} L(\hat{I}) &= log(A(\hat{I})) \\ R(\hat{I}) &= exp(L(\hat{I}) - f_{ave} * L(\hat{I})) \end{aligned} L(I^)R(I^)=log(A(I^))=exp(L(I^)faveL(I^))
其中, f a v e f_{ave} fave为均值滤波器, ∗ * 为相关操作(cross-correlation)。于是突出特征图为:
M = G σ ∗ F − 1 ( R ( I ^ ) ⋅ e x p ( j ⋅ P ( I ^ ) ) ) M = G_\sigma * F^{-1}(R(\hat{I}) \cdot exp(j \cdot P(\hat{I}))) M=GσF1(R(I^)exp(jP(I^)))

得到这个特征图后,就可以通过图像处理的连通性分析,找出突出特征的位置和boundding box。结合代码来看看:

// 一个MAT的数组,第一个放实部,第二个放虚部cv::Mat planes[] = {
cv::Mat_
(image.clone()), cv::Mat::zeros(image.size(), CV_32F) };cv::Mat complexImg;cv::merge(planes, 2, complexImg);cv::dft(complexImg, complexImg); // 做傅里叶变换,存到complexImg中cv::split(complexImg, planes); // 分成实部和虚部cv::Mat mag, logmag, smooth, spectralResidual;cv::magnitude(planes[0], planes[1], mag); // 计算幅值A(I)cv::log(mag, logmag); // => log(sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))cv::boxFilter(logmag, smooth, -1, cv::Size(average_filter_size, average_filter_size)); // 对log幅值均值滤波cv::subtract(logmag, smooth, spectralResidual); cv::exp(spectralResidual, spectralResidual);// 最终得到 M = exp(log(A(I)) - f_ave * log(A(I)))// real part planes[0] = planes[0].mul(spectralResidual) / mag;// imaginary part planes[1] = planes[1].mul(spectralResidual) / mag;// 上面两句下面note解释。cv::merge(planes, 2, complexImg);cv::dft(complexImg, complexImg, cv::DFT_INVERSE | cv::DFT_SCALE); // 傅里叶反变换cv::split(complexImg, planes);cv::magnitude(planes[0], planes[1], mag);cv::multiply(mag, mag, mag); // 获得最终特征图cv::GaussianBlur(mag, mag, cv::Size(5, 5), 8, 8); // 高斯滤波

note:

上面两句是怎么和公式对应的呢?
R ( I ^ ) ⋅ e x p ( j ⋅ P ( I ^ ) ) = R ( I ^ ) ⋅ A ( I ^ ) e j ⋅ P ( I ^ ) / A ( I ^ ) = R ( I ^ ) ⋅ ( R e ( I ^ ) + I m ( I ^ ) ) / A ( I ^ ) = R ( I ^ ) ⋅ R e ( I ^ ) / A ( I ^ ) + R ( I ^ ) ⋅ I m ( I ^ ) / A ( I ^ ) \begin{aligned} R(\hat{I}) \cdot exp(j\cdot P(\hat{I})) &= R(\hat{I}) \cdot A(\hat{I}) e^{j\cdot P(\hat{I})} / A(\hat{I}) \\ &= R(\hat{I}) \cdot (Re(\hat{I}) + Im(\hat{I})) / A(\hat{I}) \\ &= R(\hat{I}) \cdot Re(\hat{I}) / A(\hat{I}) + R(\hat{I}) \cdot Im(\hat{I}) / A(\hat{I}) \end{aligned} R(I^)exp(jP(I^))=R(I^)A(I^)ejP(I^)/A(I^)=R(I^)(Re(I^)+Im(I^))/A(I^)=R(I^)Re(I^)/A(I^)+R(I^)Im(I^)/A(I^)
于是得到上两句公式!!


        初步检测到特征图后,作者需要对特征图进行筛选。为此作者设计了两个指标:

  1. 对比密度(contrast density)
    ϕ x = ( x − x ‾ ) 2 ‾ \phi_x = \overline{(x-\overline{x})^2} ϕx=(xx)2
  2. 边缘复杂度(edge complexity)
    ρ x = C ( x ) S ( x ) \rho_x = \frac{C(x)}{S(x)} ρx=S(x)C(x)

其中,x是提取的特征区域, x ‾ \overline{x} x是这个区域的平均像素,C是canny边缘滤波器,S为这个区域像素的个数。看看代码:

cv::Mat object;		// 特征区域image_float(cv::Rect(x, y, w, h)).copyTo(object);		// check contrast density float mean = cv::sum(object).val[0] / (w*h);	// 像素均值float cov = 0.0;for (int p = 0;p < h;p++) {
for (int q = 0;q < w;q++) {
cov += fabs(object.at
(p, q) - mean); }}cov = cov / (w*h);// 这里用了绝对值距离,并除以了像素总数,和论文貌似不符?if (cov < min_cov) continue;// check egde complexitycv::Mat edge;cv::Laplacian(object, edge, CV_32F, 3);float edge_cov = 0;// 这里用了拉式算子检测边缘,边缘响应强度大于50就算作检测到边缘for (int p = 0;p < h;p++) {
for (int q = 0;q < w;q++) {
if(abs(edge.at
(p, q))>50) edge_cov += 1; }}edge_cov = edge_cov / (w*h);if (edge_cov < min_edge_cov) continue;

2. 突出特征匹配

        作者使用了核相关滤波(kernel cross-correlation)的方法进行两个突出特征块的匹配。此方法能够在频域直接进行信号匹配,而且具有位移、旋转和尺度不变性。其实相关滤波的方法在目标跟踪领域非常流行,如MOSSE、KCF和DSST这些算法都是使用相关滤波做的。

        相关滤波的基本思想就是,对于 g = x ∗ h g = x * h g=xh x x x为图像信号, h h h为滤波器, ∗ * 为相关操作),找到一个最佳的滤波器h,使得输出g是期望输出。而相关操作在频域上计算相当快。

        规定一下符号: ( ⋅ ) ^ \hat{(\cdot)} ()^为傅里叶变换, ∗ , ⨀ , h ∗ *, \bigodot, h^* ,,h分别表示相关、按元素乘积和共轭。于是核相关定义为:

在这里插入图片描述

x和z文中说分别是候选(存在数据库)特征和当前特征,但我认为是相反的。在代码部分我会说明我的理由,暂时先按我的理解。公式中的 z i j z_{ij} zij表示特征块水平平移i个像素垂直平移j个像素,这是为了实现平移不变性的,正确的平移位置应该会有更大的响应。这个核函数是高斯核,代入高斯核后,这个 k z ( x ) k_z(x) kz(x)就可以通过下式得到:

在这里插入图片描述
不过这里为什么计算复杂度从平方变到对数这里我不太清楚。

        现在知道 k z ( x ) k_z(x) kz(x)怎么计算,但还有最优滤波器 h ^ ∗ \hat{h}^* h^未知。这个滤波器可以看成是一个优化问题,使用平方和误差(SSE)进行优化:

在这里插入图片描述

从(10)式看到,这个滤波器有闭式解,因此可以在线求解,而不需要离线准备一个预训练的数据库。作者在求h的过程中,令x=z,而g则是使用了下式决定:
g ( x , y ) = { 1   ,   i f    x = 0 , y = 0 0   ,         o t h e r w i s e g(x,y) = \left\{ \begin{aligned} 1 \ &, \ if \ \ x=0, y=0\\ 0 \ &, \ \ \ \ \ \ \ otherwise \end{aligned} \right. g(x,y)={
1 0 , if  x=0,y=0,       otherwise

但代码好像也不是用这个g的,下面会说。

        最后就是相似性评价,因为最后g的输出肯定不可能和期望输出一模一样,所以取g的最大响应作为相似性得分:

在这里插入图片描述


        梳理一下步骤,看下图:

在这里插入图片描述
上面那一行,代表存在数据库的图片,里面有一个标志牌的突出特征图像块,做傅里叶变换后训练 h ^ ∗ \hat{h}^* h^,由闭式解(10)能求出这个突出特征对应的最优滤波器( k ^ z ( x ) \hat{k}_z(x) k^z(x)是由x=z计算的),存在数据库。下面那一行是当前图片,检测突出特征,傅里叶变换,然后计算 k ^ z ( x ) \hat{k}_z(x) k^z(x)(而这里x是当前突出特征块,z是数据库突出特征块),最后计算 k ^ z ( x ) \hat{k}_z(x) k^z(x) h ^ ∗ \hat{h}^* h^的乘积,傅里叶反变换后得到响应 g g g,在求其最大值得到相似性评分。


        下面看看代码:

// 查询数据库for (int i = 0;i < image_database.size();i++) {
// 先把目标特征resize到和数据库特征尺寸大小一样 cv::resize(cvimage, cvimage, cv::Size(image_database[i].salient_region.cols(), image_database[i].salient_region.rows())); Eigen::Map
image_eigen(&cvimage.at
(0, 0), cvimage.cols, cvimage.rows);; // 计算相似性 float similarity = kcc_test(image_database[i].salient_region_fft, fft(image_eigen.transpose()), image_database[i].h_hat_star); similarity_matrix(sr.frame_num, image_database[i].frame_num) = max(similarity_matrix(sr.frame_num, image_database[i].frame_num), similarity); if (similarity > loop_threshold && abs(sr.frame_num- image_database[i].frame_num)> frame_space) {
result.push_back(Loop(sr.frame_num, image_database[i].frame_num, similarity)); } if (max_similarity < similarity) max_similarity = similarity;}

上面做的事就是对于目标特征块cvimage,不断查询数据库,然后通过函数kcc_test计算相似性。下面看kcc_test函数:

ArrayXXf xy = ifft(image_train * image_test.conjugate());	// test里用的x和z是不同的ArrayXXf xxyy = (xx + yy - 2 * xy) / N;for (int i = 0;i < xxyy.rows();i++) {
for (int j = 0;j < xxyy.cols();j++) {
if (xxyy(i, j) < 0) xxyy(i, j) = 0; }}ArrayXXcf kxy_hat = fft((-1 / (sigma*sigma)*xxyy).exp());// 以上计算\hat{k}_z(x) ArrayXXf result = ifft(kxy_hat*h_hat_star);// 这里这个h_hat_star(最优滤波器),是作为参数传进来的,是kcc_train函数训练出来的,每个数据库特征块都有一个独自的最优滤波器。float max_num = 0.0;// 求g的最大值作为相似性评分for (int i = 0;i < xxyy.rows()/6;i++) {
for (int j = 0;j < xxyy.cols()/6;j++) {
if (result(i, j) > max_num) max_num = result(i, j); }}//std::cout << result.row(0) << std::endl;return max_num;

下面看看kcc_train,这个函数计算耗时很少,因此可以在线计算:

int N = image_fft.rows()*image_fft.cols();ArrayXXf gaussian_mask = ArrayXXf::Zero(image_fft.rows(), image_fft.cols());// 这里为什么除以49我也不清楚float row_sum = image_fft.rows()*image_fft.rows() / 49.0;float col_sum = image_fft.cols()*image_fft.cols() / 49.0;for (int i = 0;i < image_fft.rows();i++) {
for (int j = 0;j < image_fft.cols();j++) {
gaussian_mask(i,j) = exp(-((i+1)*(i+1)/ row_sum +(j+1)*(j+1)/ col_sum)/2); }}ArrayXXcf g_hat = fft(gaussian_mask);// 这里使用了帕森瓦尔定理,在时域图像的像素平方和等于在频域的平方和除以一个常数float xx = image_fft.abs2().sum() / N; // Parseval's TheoremArrayXXf xy = ifft(image_fft * image_fft.conjugate());// 这里train用到的x和z都是xArrayXXf xxyy = (xx + xx - 2 * xy) / N;for (int i = 0;i < xxyy.rows();i++) {
for (int j = 0;j < xxyy.cols();j++) {
if (xxyy(i, j) < 0) xxyy(i, j) = 0; }}ArrayXXcf kxx_hat = fft((-1 / (sigma*sigma)*xxyy).exp());h_hat_star = g_hat / (kxx_hat + lambda);

note:

这里 g ^ \hat{g} g^是通过对gaussian_mask的傅里叶变换得到的,然而gaussuan_mask的表达式为:
g ( x , y ) = e − ( x 2 r 2 + y 2 c 2 ) / 2 g(x,y) = e^{-(\frac{x^2}{r^2} + \frac{y^2}{c^2})/2} g(x,y)=e(r2x2+c2y2)/2
虽然和原来的期望响应也有共同之处,就是在(0,0)处响应是最大的,但还是有所区别。

3. 一致性检验

        经过上面两个步骤得到的回环检测只是对于当前帧的候选回环,至于是不是真正的回环,还需要经过一个检验。检验的方法非常简单,当前帧和候选帧进行ORB特征提取和匹配,设定一个阈值,如果匹配点大于这个阈值就说明当前帧构成回环。

实验

1. 突出特征检测

在这里插入图片描述

        左、中、右分别是原图,特征检测图和在这基础上进行特征筛选的图。效果感觉一般般,确实有些显著的特征提取了出来。特征筛选后也有部分较差的特征块被去掉。

2. 闭环检测实验

        我使用了New College 数据集进行实验,但效果好像不是太好,我手动调了一下参数好像也不太行。

在这里插入图片描述

但作者的效果是:

在这里插入图片描述

暂时搞不出来,在问作者中ing。。。

参考文献

[1]

转载地址:http://qoklf.baihongyu.com/

你可能感兴趣的文章
阿里云《云原生》公开课笔记 第六章 应用编排与管理:Deployment
查看>>
阿里云《云原生》公开课笔记 第七章 应用编排与管理:Job和DaemonSet
查看>>
阿里云《云原生》公开课笔记 第八章 应用配置管理
查看>>
阿里云《云原生》公开课笔记 第九章 应用存储和持久化数据卷:核心知识
查看>>
linux系统 阿里云源
查看>>
国内外helm源记录
查看>>
牛客网题目1:最大数
查看>>
散落人间知识点记录one
查看>>
Leetcode C++ 随手刷 547.朋友圈
查看>>
手抄笔记:深入理解linux内核-1
查看>>
内存堆与栈
查看>>
Leetcode C++《每日一题》20200621 124.二叉树的最大路径和
查看>>
Leetcode C++《每日一题》20200622 面试题 16.18. 模式匹配
查看>>
Leetcode C++《每日一题》20200625 139. 单词拆分
查看>>
Leetcode C++《每日一题》20200626 338. 比特位计数
查看>>
Leetcode C++ 《拓扑排序-1》20200626 207.课程表
查看>>
Go语言学习Part1:包、变量和函数
查看>>
Go语言学习Part2:流程控制语句:for、if、else、switch 和 defer
查看>>
Go语言学习Part3:struct、slice和映射
查看>>
Go语言学习Part4-1:方法和接口
查看>>