开源词袋模型DBow3原理&源码

前人摘树,后人乘凉。

源码在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了。

字典长这样:

 1 vocabulary:
 2    k: 9   //聚类中心数
 3    L: 3   //层数
 4    scoringType: 0
 5    weightingType: 0
 6    nodes:
 7       - { nodeId:19, parentId:10, weight:2.8768207245178085e-01,
 8           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  }
 9       - { nodeId:20, parentId:10, weight:1.3862943611198906e+00,
10           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  }
11       - { nodeId:21, parentId:10, weight:0.,
12           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  }
13       - { nodeId:22, parentId:10, weight:0.,
14           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  }
15       - { nodeId:23, parentId:10, weight:1.3862943611198906e+00,
16           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  }
17 words:
18       - { wordId:0, nodeId:19 }
19       - { wordId:1, nodeId:20 }
20       - { wordId:2, nodeId:21 }
21       - { wordId:3, nodeId:22 }
22       - { wordId:4, nodeId:23 }
23       - { wordId:5, nodeId:24 }

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

检索入口描述:

 1 database:
 2    nEntries: 4
 3    usingDI: 0
 4    diLevels: 0
 5    invertedIndex:
 6       -  //(wordId:0)
 7          - { imageId:1, weight:1.6807896319101980e-03 }
 8          - { imageId:2, weight:3.2497152852064880e-03 }
 9          - { imageId:3, weight:3.6665308718065778e-03 }
10       -  //(wordId:1)
11          - { imageId:1, weight:4.0497295661974788e-03 }
12       -  //(wordId:2)
13          []
14       -
15          []
16       -
17          - { imageId:2, weight:3.9149658655580431e-03 }
18       -
19          - { imageId:3, weight:4.4171079458813099e-03 }
20       -
21          - { imageId:1, weight:2.0248647830987394e-03 }
22          - { 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函数,注释:

 1 void Database::queryL1(const BowVector &vec, QueryResults &ret, int max_results, int max_id) const
 3 {
 4   BowVector::const_iterator vit;
 5
 6   std::map<EntryId, double> pairs;
 7   std::map<EntryId, double>::iterator pit;
 8
 9   for(vit = vec.begin(); vit != vec.end(); ++vit)
10   {
11     const WordId word_id = vit->first;
12     const WordValue& qvalue = vit->second;
13
14     const IFRow& row = m_ifile[word_id];
15
16     // IFRows are sorted in ascending entry_id order
18     for(auto rit = row.begin(); rit != row.end(); ++rit)
19     {
20       const EntryId entry_id = rit->entry_id;
21       const WordValue& dvalue = rit->word_weight;
22
23       if((int)entry_id < max_id || max_id == -1)
24       {
25         double value = fabs(qvalue - dvalue) - fabs(qvalue) - fabs(dvalue);
26
27         pit = pairs.lower_bound(entry_id);
28         if(pit != pairs.end() && !(pairs.key_comp()(entry_id, pit->first)))
29         {
30           pit->second += value;  //如果已经有entry_id,累加和
31         }
32         else
33         {                        //如果没有,插入此id
34           pairs.insert(pit, std::map<EntryId, double>::value_type(entry_id, value));
35         }
36       }
38     } // for each inverted row
39   } // for each query word
40
41   // move to vector
42   ret.reserve(pairs.size());
43   for(pit = pairs.begin(); pit != pairs.end(); ++pit)
44   {
45     ret.push_back(Result(pit->first, pit->second));
46   }
47
48   // resulting "scores" are now in [-2 best .. 0 worst]
50   // sort vector in ascending order of score
51   std::sort(ret.begin(), ret.end());
52   // (ret is inverted now --the lower the better--)
54   // cut vector
55   if(max_results > 0 && (int)ret.size() > max_results)
56     ret.resize(max_results);
57
58   // complete and scale score to [0 worst .. 1 best]
59   // ||v - w||_{L1} = 2 + Sum(|v_i - w_i| - |v_i| - |w_i|)
60   //        for all i | v_i != 0 and w_i != 062   // scaled_||v - w||_{L1} = 1 - 0.5 * ||v - w||_{L1}
63   QueryResults::iterator qit;
64   for(qit = ret.begin(); qit != ret.end(); qit++)
65     qit->Score = -qit->Score/2.0;
66 }

原文地址:https://www.cnblogs.com/zhengmeisong/p/8446687.html

时间: 2025-01-07 22:47:44

开源词袋模型DBow3原理&源码的相关文章

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

util里提供了create_voc_step0用于批量生成features并保存,create_voc_step1读入features再生成聚类中心,比较适合大量语料库聚类中心的生成. 提取一张图的特征如下: 第一行是文件头,分别用32bit表示特征来自几张图(1).特征描述子长度(128bit,=32B), 特征长度(89), 特征类型(cv8u) ./utils/create_voc_step0 orb fea0 zs00.jpg Extracting features... readin

【141030】金山卫士开源代码,VC++完整源码

VC++金山卫士开源代码,包含所有模块的源码,促进互联网行业的开源计划 :也是你学习VC++的绝佳范例,可以接触到中国最专业的安全类软件源代码,你可以自由的使用/研究/修订/再发布 这些代码以及延伸作品.VC++金山卫士开源代码下载. 游戏源码下载地址:点击下载

防猎豹垃圾清理(实现原理+源码)

防猎豹垃圾清理(实现原理+源码) 转载请注明出处: 防猎豹垃圾清理(实现原理+源码) 前几天无意打开猎豹内存大师, 发现它的垃圾清理很强大, 效果也不错, 闲着就研究了下. 不过.. 结果貌似和我想象的不太一样.怎么说呢, 听我下文一一分析. 效果图: 从效果图, 我们可以看出它有以下几个功能: 获取设备上已安装的所有App 获取App的信息, 包括图标和名称 获取当前已用存储和可用存储 扫描App动画效果 清除所有App垃圾文件 看到这里, 你是不是也觉得很强大? 然后然后, 感叹的同时, 我

App开源分享-在路上项目源码

App开源分享-在路上项目源码 在路上是一款旅游型的APP,是集旅游旅游目的地攻略指南,可以自动定位到城市,景点,餐馆,酒店,还可以分享旅游经验,与参与者互动.架构合理,有详细的注解.很好的学习材料. 下载地址:http://www.devstore.cn/code/info/905.html 运行截图:    

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

百度uid-generator源码 https://github.com/baidu/uid-generator snowflake算法 uid-generator是基于Twitter开源的snowflake算法实现的. snowflake将long的64位分为了3部分,时间戳.工作机器id和序列号,位数分配如下. 其中,时间戳部分的时间单位一般为毫秒.也就是说1台工作机器1毫秒可产生4096个id(2的12次方). 源码实现分析 与原始的snowflake算法不同,uid-generator支

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

前言 在上一篇中,我们讲解了哈勃沙箱的技术点,详细分析了静态检测和动态检测的流程.本篇接着对动态检测的关键技术点进行分析,包括strace,sysdig,volatility.volatility的介绍不会太深入,内存取证这部分的研究还需要继续. strace机制 上一篇讲到了strace和ltrace都是基于ptrace机制,但是对ptrace机制和strace/ltrace是如何利用ptrace监控系统调用,没有进行详细的讲解.   那什么是ptrace机制呢? ptrace机制是操作系统提

Java HashMap实现原理 源码剖析

HashMap是基于哈希表的Map接口实现,提供了所有可选的映射操作,并允许使用null值和null建,不同步且不保证映射顺序.下面记录一下研究HashMap实现原理. HashMap内部存储 在HashMap内部,通过维护一个 瞬时变量数组table (又称:桶) 来存储所有的键值对关系,桶 是个Entry对象数组,桶 的大小可以按需调整大小,长度必须是2的次幂.如下代码: /** * 一个空的entry数组,桶 的默认值 */ static final Entry<?,?>[] EMPTY

Mybatis Interceptor 拦截器原理 源码分析

Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件. 代理链的生成 Mybatis支持对Executor.StatementHandler.PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理. 通过查看Configuration类的源代码我们可以看到,每次都

开源中国 OsChina Android 客户端源码分析(8)数据库Sqlite

1开源中国客户端使用的数据库部分的源码在net.oschina.app.db包下,两个类一个是用于管理数据库的创建类DatabaseHelper,继承SQLiteOpenHelper,另一个是用于数据库的增删改查的工具类NoteDatabase.那么数据库在开源中国源码中哪一模块用到了呢? 便签管理,便签是什么?就是一个记事本的功能o(^▽^)o 2关于SQLiteOpenHelper的使用,自己之前的项目中没有用到过,看了下,这里有个体会:当获取到SQLiteOpenHelper实例,并使用g