前人摘树,后人乘凉。

源码在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. 解决采集知乎数据时由于账号被封遗漏的账号重爬问题(python代码)

    '''一.最笨的办法了################################################################为了处理由于账号被封而没跑到的问题id进行以下两步 ...

  2. tomcat停止和启动脚本

    日常重启tomcat比较麻烦,所以写了2个脚本,在脚本后输入tomcat名称即可 启动或重启tomcat #!/bin/sh TOMCAT_HOME=/usr/java/$1 if [ ! -n &q ...

  3. what's the python之字符编码与文件处理

    用文本编辑器打开一个文件就是把一个文件读入了内存中 ,所以打开文件的操作也是在内存中的,断电即消失,所以若要保存其内容就必须点击保存让其存入硬盘中 python解释器执行py文件的原理 : 第一阶段: ...

  4. 细细探究MySQL Group Replicaiton — 配置维护故障处理全集(转)

    如果转载,请注明博文来源: www.cnblogs.com/xinysu/   ,版权归 博客园 苏家小萝卜 所有.望各位支持! 

  5. 正交表和TCG的使用

    正交表法是一种有效减少测试用例个数的设计方法. 正交表法的依据是Galois理论,从大量的实验数据中挑选适量的.有代表性的点,从而合理的安排实验的一种科学实验设计方法.在测试用例的设计中,可以从大量的 ...

  6. MySql使用笔记

    mysql版本信息:mysqld --version Ver 5.6.24 在服务里面看不到mysql 安装服务: mysqld.exe -install 启动服务: net start mysql ...

  7. golang fmt格式“占位符”

    # 定义示例类型和变量 type Human struct { Name string } var people = Human{Name:"zhangsan"} 普通占位符 占位 ...

  8. 微信小程序--修改data数组或对象里面的值

    1.初始data数据 Page({     data:{          code:'1234',         reward:[{             name:"艾伦" ...

  9. NeuroNER+brat工具学习

    1.Brat:http://brat.nlplab.org/ 能够进行直觉标注.命名实体识别.关系标注.分块.共存标注.二元关系标注等(药物与药物).时间标注. 但是这个安装好麻烦啊... 2.

  10. Element-ui的表单中怎么添加正则校验

    1. 以中国大陆手机号验证为例 // 这是组价的代码 <el-form-item prop="mobile"> <el-input type="text ...