前人摘树,后人乘凉。

源码在github有CMakeLists,代码下下来可以直接编译。

泡泡机器人有个很详细的分析,结合浅谈回环检测中的词袋模型,配合高翔的回环检测应用,基本上就可以串起来了。

tf-idf的概念,表达方式不唯一,这里的定义是这样:

tf表示词频,这个单词在图像中出现的次数/图像单词总量

idf表示单词在整个训练语料库中的常见程度:idf=log(N/Ni),N是训练语料库中的图片总数,Ni是训练语料库中包含这个单词的图像数

由于Ni<=N,idf>=0,当Ni=N时,idf取最小值0(如果一个单词在每个语料库里都出现了,这个单词太过常见可以忽略)

视觉字典生成以后idf就固定了,检索数据库时的权重用tf*idf计算。

个人理解DBow3里单词(word Id)对应的权值就是idf,检索入口(Entry Id)处的权值是tf*idf。

demo的使用:./demo_gernal orb image1 image2 image3 image4

会在执行目录下生成用输入的4张图提特征的voc字典,并以这四张图为检索入口,生成db检索库,db文件里包含字典,所以有了db就不用voc了。

字典长这样:

 vocabulary:
k: 9 //聚类中心数
L: 3 //层数
scoringType: 0
weightingType: 0
nodes:
- { nodeId:19, parentId:10, weight:2.8768207245178085e-01,
descriptor:dbw3 0 32 124 169 185 96 221 205 85 157 235 189 172 8 159 181 72 50 137 222 236 88 26 107 250 49 251 221 21 127 210 106 198 42 }
- { nodeId:20, parentId:10, weight:1.3862943611198906e+00,
descriptor:dbw3 0 32 120 164 24 104 249 61 80 95 115 27 172 24 31 147 64 248 145 152 76 56 26 111 82 23 219 223 17 125 226 200 202 42 }
- { nodeId:21, parentId:10, weight:0.,
descriptor:dbw3 0 32 124 165 248 102 209 109 83 25 99 173 173 8 115 241 72 50 16 222 68 56 26 114 248 2 255 205 37 121 226 234 198 34 }
- { nodeId:22, parentId:10, weight:0.,
descriptor:dbw3 0 32 36 161 252 81 224 233 97 139 235 53 108 40 151 199 204 26 3 158 110 24 18 248 234 65 205 152 53 117 194 200 198 162 }
- { nodeId:23, parentId:10, weight:1.3862943611198906e+00,
descriptor:dbw3 0 32 76 140 56 218 221 253 113 218 95 53 44 24 24 211 106 116 81 154 18 24 18 26 122 245 95 29 17 109 146 137 212 106 }
words:
- { wordId:0, nodeId:19 }
- { wordId:1, nodeId:20 }
- { wordId:2, nodeId:21 }
- { wordId:3, nodeId:22 }
- { wordId:4, nodeId:23 }
- { wordId:5, nodeId:24 }

是对聚类生成的树的描述,树上的每一个点都是node,只有叶子结点才是word,每张图进来提取特征,利用descriptor算特征距离,最终落到叶子上(单词),所有特征的单词构成该图片的词向量。

检索入口描述:

 database:
nEntries: 4
usingDI: 0
diLevels: 0
invertedIndex:
- //(wordId:0)
- { imageId:1, weight:1.6807896319101980e-03 }
- { imageId:2, weight:3.2497152852064880e-03 }
- { imageId:3, weight:3.6665308718065778e-03 }
- //(wordId:1)
- { imageId:1, weight:4.0497295661974788e-03 }
- //(wordId:2)
[]
-
[]
-
- { imageId:2, weight:3.9149658655580431e-03 }
-
- { imageId:3, weight:4.4171079458813099e-03 }
-
- { imageId:1, weight:2.0248647830987394e-03 }
- { imageId:3, weight:4.4171079458813099e-03 }

检索入口:

根据voc字典里的描述,word id 2对应node id 21, 而node id 21对应的权值为0,也就是说word 2太普通了,在用来生成视觉词汇表的4张图里都出现了(参考中文文章里的“的”、“在”、“和”等常见词),不具有代表性, 于是根本就没有对应入口id,这是合理的。

开源出来的代码不是对相同word的入口进行加1投票,而是直接计算单词对应的所有EntryId分数,最后排序取前n个。分数可以有L1 L2 KL等几种计算方式

queryL1,C++不熟看了半天,用到map函数,注释:

 void Database::queryL1(const BowVector &vec, QueryResults &ret, int max_results, int max_id) const
{
BowVector::const_iterator vit; std::map<EntryId, double> pairs;
std::map<EntryId, double>::iterator pit; for(vit = vec.begin(); vit != vec.end(); ++vit)
{
const WordId word_id = vit->first;
const WordValue& qvalue = vit->second; const IFRow& row = m_ifile[word_id]; // IFRows are sorted in ascending entry_id order
for(auto rit = row.begin(); rit != row.end(); ++rit)
{
const EntryId entry_id = rit->entry_id;
const WordValue& dvalue = rit->word_weight; if((int)entry_id < max_id || max_id == -)
{
double value = fabs(qvalue - dvalue) - fabs(qvalue) - fabs(dvalue);
//pairs是一个map,low_bound返回首个不小于entry_id的迭代器,因此如果map里有这个entry_id, pit指向它,否则指向pairs.end()
pit = pairs.lower_bound(entry_id); //因为在db库里entry_id是按顺序存的,所以可以这样查找
if(pit != pairs.end() && !(pairs.key_comp()(entry_id, pit->first)))
{
pit->second += value; //如果已经有entry_id,累加和
}
else
{ //如果没有,插入此id
pairs.insert(pit, std::map<EntryId, double>::value_type(entry_id, value));
}
}
} // for each inverted row
} // for each query word // move to vector
ret.reserve(pairs.size());
for(pit = pairs.begin(); pit != pairs.end(); ++pit)
{
ret.push_back(Result(pit->first, pit->second));
} // resulting "scores" are now in [-2 best .. 0 worst]
// sort vector in ascending order of score
std::sort(ret.begin(), ret.end());
// (ret is inverted now --the lower the better--)
// cut vector
if(max_results > && (int)ret.size() > max_results)
ret.resize(max_results); // complete and scale score to [0 worst .. 1 best]
// ||v - w||_{L1} = 2 + Sum(|v_i - w_i| - |v_i| - |w_i|)
// for all i | v_i != 0 and w_i != 0 // scaled_||v - w||_{L1} = 1 - 0.5 * ||v - w||_{L1}
QueryResults::iterator qit;
for(qit = ret.begin(); qit != ret.end(); qit++)
qit->Score = -qit->Score/2.0;
}

-------------------2018.02.23 打印生成的orb特征----------------

输入2张图,提orb特征,描述子保存在features[0]、features[1]里,一张图500个特征点,每个点256维描述子(分成8bit*32),显示描述子竟然写了半天,我这弱渣的代码能力...

     cout<<"features size:"<<features.size()<<endl;        //feature定义:vector<cv::Mat>
cout<<"feautres[0] size:"<<features[].size()<<endl;
for(int i=;i<features[].rows;i++)
{
for(int j=;j<features[].cols;j++)
{ //十六进制显示
cout<<hex<<setfill('')<<setw()<<int( features[].at<unsigned char>(i,j) )<<' ';
}
cout<<endl;
}

扩展:

dbow3支持4种描述类型:orb brisk akaze(opencv3) surf(编译选项USE_CONTRIB)

描述子orb: 32CV_8U(256bit) brisk 64CV_8U(512bit) akaze 61CV_8U(默认DESCRIPTOR_MLDB也是二进制描述子,根据a-kaze论文,描述子长度为486bit(61*8bit) )

选择akaze描述子,生成的字典中描述子长度变为61:

-----------2019.04.04-----------

回顾了下去年这个项目:

1. 没有用到正序索引:

开源词袋模型DBow3原理&源码(一)整体结构的更多相关文章

  1. 开源词袋模型DBow3原理&源码(二)ORB特征的保存和读取

    util里提供了create_voc_step0用于批量生成features并保存,create_voc_step1读入features再生成聚类中心,比较适合大量语料库聚类中心的生成. 提取一张图的 ...

  2. 深入理解Faiss 原理&源码 (一) 编译

    目录 深入理解Faiss 原理&源码 (一) 编译 mac下安装 安装mac xcode工具包 安装 openblas 安装swig 安装libomp 编译faiss 附录 深入理解Faiss ...

  3. SharedPreferences 原理 源码 进程间通信 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. Laya Timer原理 & 源码解析

    Laya Timer原理 & 源码解析 @author ixenos 2019-03-18 16:26:38 一.原理 1.将所有Handler注册到池中 1.普通Handler在handle ...

  5. 百度开源分布式id生成器uid-generator源码剖析

    百度uid-generator源码 https://github.com/baidu/uid-generator snowflake算法 uid-generator是基于Twitter开源的snowf ...

  6. 一个Python开源项目-哈勃沙箱源码剖析(下)

    前言 在上一篇中,我们讲解了哈勃沙箱的技术点,详细分析了静态检测和动态检测的流程.本篇接着对动态检测的关键技术点进行分析,包括strace,sysdig,volatility.volatility的介 ...

  7. 开源分布式数据库中间件MyCat源码分析系列

    MyCat是当下很火的开源分布式数据库中间件,特意花费了一些精力研究其实现方式与内部机制,在此针对某些较为重要的源码进行粗浅的分析,希望与感兴趣的朋友交流探讨. 本源码分析系列主要针对代码实现,配置. ...

  8. Mybatis Interceptor 拦截器原理 源码分析

    Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最 ...

  9. 带着萌新看springboot源码11(springboot启动原理 源码上)

    通过前面这么多讲解,springboot原理应该也大概有个轮廓了,一些基本的配置,从客户端url到controller(配置一些要用的组件,servlet三大组件,处理器映射器,拦截器,视图解析器这些 ...

随机推荐

  1. CentOS SSH免密登陆

    #环境说明客户机:Mac OS X服务器:CentOS 6.5客户端:OpenSSH,OS X及大多数Linux都内置了OpenSSH.’ssh -v’命令可以查看版本. #大致流程1.在客户机创建一 ...

  2. Linux I/O 调度器

    每个块设备或者块设备的分区,都对应有自身的请求队列,  而每个请求队列都可以选择一个I/O调度器来协调所递交的.I/O调度器的基本目的是将请求按照它们对应在块设备上的扇区号进行排列,以减少磁头的移动, ...

  3. MATLAB中产生随机数的那些函数

    1.产生从imin~imax的m*n矩阵 randi([imin,imax],m,n); 2.产生1~n的无重复随机整数 randperm(n);

  4. python-面向对象-12_模块和包

    模块和包 目标 模块 包 发布模块 01. 模块 1.1 模块的概念 模块是 Python 程序架构的一个核心概念 每一个以扩展名 py 结尾的 Python 源代码文件都是一个 模块 模块名 同样也 ...

  5. 用A标签实现页面内容定位 点击链接跳到具体位置

    经常在维基百科等网站看到目录列表,点击链接会跳到具体的位置,小美眉一直在问是怎么做到的,其实挺简单的,用A标签实现页面内容定位就行了.实例参考微信营销理论手册的目录. 首先用A标签定义目录的链接. & ...

  6. 运维自动化工具ansible

    企业级自动化运维工具应用实战ansible 公司计划在年底做一次大型市场促销活动,全面冲刺下交易额,为明年的上市做准备.公司要求各业务组对年底大促做准备,运维部要求所有业务容量进行三倍的扩容,并搭建出 ...

  7. Got timeout reading communication packets解决方法

    Got timeout reading communication packets解决方法 http://www.th7.cn/db/mysql/201702/225243.shtml [Note] ...

  8. MACD回零轴有三种方式

    MACD回零轴三种方式 MACD上双线回抽或者回档到0轴附近: 第一主动回零轴. 第二被动回零轴. 第三单N回零轴. 随后的走势第二种涨幅最猛.第三种级别最大. 这里要正确理解背离.背离有三种.1,指 ...

  9. Python3学习之路~0 目录

    目录 Python3学习之路~2.1 列表.元组操作 Python3学习之路~2.2 简单的购物车程序 Python3学习之路~2.3 字符串操作 Python3学习之路~2.4 字典操作 Pytho ...

  10. 010-java 表单方式或者base64方式上传图片,后端使用nutz的post转发图片到另一个请求

    本地上传图片 方式一.使用表单方式上传-enctype <form enctype="multipart/form-data" method="post" ...