GSLAM  3.0.0
Vocabulary.h
1 // GSLAM - A general SLAM framework and benchmark
2 // Copyright 2018 PILAB Inc. All rights reserved.
3 // https://github.com/zdzhaoyong/GSLAM
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors may be
14 // used to endorse or promote products derived from this software without
15 // specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 //
29 // Author: zd5945@126.com (Yong Zhao)
30 //
31 // Vocabulary is an accelerated version of DBoW: https://github.com/rmsalinas/DBow3
32 
33 #ifndef GSLAM_VOCABULARY_H
34 #define GSLAM_VOCABULARY_H
35 
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <limits.h>
39 #include <map>
40 #include <vector>
41 #include <cfloat>
42 #include <cmath>
43 #include <string>
44 #include <sstream>
45 #include <iostream>
46 #include <numeric> // std::accumulate
47 #include <chrono>
48 #include <fstream>
49 #include <bitset>
50 #include <memory>
51 
52 #ifdef __x86_64__
53 #include <xmmintrin.h>
54 #include <emmintrin.h>
55 #endif
56 
57 #include "GSLAM/core/GImage.h"
58 #include "GSLAM/core/Messenger.h"
59 
60 #define GSLAM_VOCABULARY_KMAX 10
61 
62 namespace GSLAM {
63 
64 typedef size_t NodeId;
65 typedef size_t WordId;
66 typedef float WordValue;
67 typedef std::map<WordId,float> BowVector;
68 typedef std::map<WordId,std::vector<unsigned int> > FeatureVector;
69 
70 
71 typedef GImage TinyMat;
72 
73 class GeneralScoring;
75 {
76 public:
77  friend class GBoW;
78 
79 
80  /// L-norms for normalization
81  enum LNorm
82  {
83  L1,
84  L2
85  };
86 
87  /// Weighting type
89  {
90  TF_IDF,
91  TF,
92  IDF,
93  BINARY
94  };
95 
96  /// Scoring type
98  {
99  L1_NORM,
100  L2_NORM,
101  CHI_SQUARE,
102  KL,
103  BHATTACHARYYA,
104  DOT_PRODUCT
105  };
106 
107 
108  /**
109  * Initiates an empty vocabulary
110  * @param k branching factor
111  * @param L depth levels
112  * @param weighting weighting type
113  * @param scoring scoring type
114  */
115  Vocabulary(int k = 10, int L = 5,
116  WeightingType weighting = TF_IDF, ScoringType scoring = L1_NORM);
117 
118  /**
119  * Creates the vocabulary by loading a file
120  * @param filename
121  */
122  Vocabulary(const std::string &filename);
123 
124  /**
125  * Creates a vocabulary from the training features, setting the branching
126  * factor nad the depth levels of the tree, and the weighting and scoring
127  * schemes
128  */
129  static std::shared_ptr<Vocabulary> create(const std::vector<TinyMat> &training_features,
130  int k = 10, int L = 5,WeightingType weighting = TF_IDF, ScoringType scoring = L1_NORM);
131 
132  /**
133  * Saves the vocabulary into a file. If filename extension contains .yml, opencv YALM format is used. Otherwise, binary format is employed
134  * @param filename
135  */
136  bool save(const std::string &filename, bool binary_compressed=false) const;
137 
138  /**
139  * Loads the vocabulary from a file created with save
140  * @param filename.
141  */
142  bool load(const std::string &filename);
143 
144 
145  bool load(std::istream& ifs);
146 
147  /**
148  * Returns the number of nodes in the vocabulary
149  * @return number of nodes
150  */
151  virtual inline unsigned int size() const{
152  unsigned int count=0;
153  for(auto& n:m_nodes) if(!n.childNum) count++;
154  return count;
155  }
156 
157 
158  /**
159  * Returns whether the vocabulary is empty (i.e. it has not been trained)
160  * @return true iff the vocabulary is empty
161  */
162  virtual inline bool empty() const{ return m_nodes.empty();}
163 
164  /** Clears the vocabulary object
165  */
166  void clear();
167 
168  void transform(const std::vector<TinyMat>& features, BowVector &v)const;
169  /**
170  * Transforms a set of descriptores into a bow vector
171  * @param features, one per row
172  * @param v (out) bow vector of weighted words
173  */
174  virtual void transform(const TinyMat & features, BowVector &v)
175  const;
176  /**
177  * Transform a set of descriptors into a bow vector and a feature vector
178  * @param features
179  * @param v (out) bow vector
180  * @param fv (out) feature vector of nodes and feature indexes
181  * @param levelsup levels to go up the vocabulary tree to get the node index
182  */
183  virtual void transform(const std::vector<TinyMat>& features,
184  BowVector &v, FeatureVector &fv, int levelsup=0) const;
185  /**
186  * Transform a set of descriptors into a bow vector and a feature vector
187  * @param features
188  * @param v (out) bow vector
189  * @param fv (out) feature vector of nodes and feature indexes
190  * @param levelsup levels to go up the vocabulary tree to get the node index
191  */
192  virtual void transform(const TinyMat& features,
193  BowVector &v, FeatureVector &fv, int levelsup=0) const;
194 
195  /**
196  * Transforms a single feature into a word (without weight)
197  * @param feature
198  * @return word id
199  */
200  virtual WordId transform(const TinyMat& feature) const;
201 
202  /**
203  * Returns the score of two vectors
204  * @param a vector
205  * @param b vector
206  * @return score between vectors
207  * @note the vectors must be already sorted and normalized if necessary
208  */
209  inline double score(const BowVector &a, const BowVector &b) const;
210 
211  /**
212  * Returns the id of the node that is "levelsup" levels from the word given
213  * @param wid word id
214  * @param levelsup 0..L
215  * @return node id. if levelsup is 0, returns the node id associated to the
216  * word id
217  */
218  virtual NodeId getParentNode(WordId wid, int levelsup) const;
219 
220  /**
221  * Returns the ids of all the words that are under the given node id,
222  * by traversing any of the branches that goes down from the node
223  * @param nid starting node id
224  * @param words ids of words
225  */
226  void getWordsFromNode(NodeId nid, std::vector<WordId> &words) const;
227 
228  /**
229  * Returns the branching factor of the tree (k)
230  * @return k
231  */
232  inline int getBranchingFactor() const { return m_k; }
233 
234  /**
235  * Returns the depth levels of the tree (L)
236  * @return L
237  */
238  inline int getDepthLevels() const { return m_L; }
239 
240  /**
241  * Returns the real depth levels of the tree on average
242  * @return average of depth levels of leaves
243  */
244  float getEffectiveLevels() const;
245 
246  /**
247  * Returns the descriptor of a word
248  * @param wid word id
249  * @return descriptor
250  */
251  virtual inline TinyMat getWord(WordId wid) const;
252 
253  /**
254  * Returns the weight of a word
255  * @param wid word id
256  * @return weight
257  */
258  virtual inline WordValue getWordWeight(WordId wid) const;
259 
260  /**
261  * Returns the weighting method
262  * @return weighting method
263  */
264  inline WeightingType getWeightingType() const { return m_weighting; }
265 
266  /**
267  * Returns the scoring method
268  * @return scoring method
269  */
270  inline ScoringType getScoringType() const { return m_scoring; }
271 
272  /**
273  * Changes the weighting method
274  * @param type new weighting type
275  */
276  inline void setWeightingType(WeightingType type);
277 
278  /**
279  * Changes the scoring method
280  * @param type new scoring type
281  */
282  void setScoringType(ScoringType type);
283 
284 
285  /**
286  * Stops those words whose weight is below minWeight.
287  * Words are stopped by setting their weight to 0. There are not returned
288  * later when transforming image features into vectors.
289  * Note that when using IDF or TF_IDF, the weight is the idf part, which
290  * is equivalent to -log(f), where f is the frequency of the word
291  * (f = Ni/N, Ni: number of training images where the word is present,
292  * N: number of training images).
293  * Note that the old weight is forgotten, and subsequent calls to this
294  * function with a lower minWeight have no effect.
295  * @return number of words stopped now
296  */
297  virtual int stopWords(double minWeight);
298 
299 
300  /** Returns the size of the descriptor employed. If the Vocabulary is empty, returns -1
301  */
302  int getDescritorSize()const{
303  return m_nodeDescriptors.cols;
304  }
305  /** Returns the type of the descriptor employed normally(8U_C1, 32F_C1)
306  */
307  int getDescritorType()const{
308  return m_nodeDescriptors.type();
309  }
310 
311  /**
312  * Calculates the mean value of a set of descriptors
313  * @param descriptors
314  * @param mean mean descriptor
315  */
316  static void meanValue(const std::vector<TinyMat> &descriptors,
317  TinyMat &mean) ;
318 
319  /**
320  * Calculates the distance between two descriptors
321  * @param a
322  * @param b
323  * @return distance
324  */
325  float distance(const TinyMat &a, const TinyMat &b)const;
326 
327 
328  /**
329  * Creates an instance of the scoring object accoring to m_scoring
330  */
331  void createScoringObject();
332 
333  /**
334  * Returns the word id associated to a feature
335  * @param feature
336  * @param id (out) word id
337  * @param weight (out) word weight
338  * @param nid (out) if given, id of the node "levelsup" levels up
339  * @param levelsup
340  */
341  virtual void transform(const TinyMat &feature,
342  WordId &id, WordValue &weight, NodeId* nid, int levelsup = 0) const;
343  /**
344  * Returns the word id associated to a feature
345  * @param feature
346  * @param id (out) word id
347  * @param weight (out) word weight
348  * @param nid (out) if given, id of the node "levelsup" levels up
349  * @param levelsup
350  */
351  virtual void transform(const TinyMat &feature,
352  WordId &id, WordValue &weight ) const;
353 
354  /**
355  * Returns the word id associated to a feature
356  * @param feature
357  * @param id (out) word id
358  */
359 // virtual void transform(const TinyMat &feature, WordId &id) const;
360 
361  static void addWeight(BowVector& bow,WordId id, WordValue v)
362  {
363  BowVector::iterator vit = bow.lower_bound(id);
364 
365  if(vit != bow.end() && !(bow.key_comp()(id, vit->first)))
366  {
367  vit->second += v;
368  }
369  else
370  {
371  bow.insert(vit, BowVector::value_type(id, v));
372  }
373  }
374 
375  static void addIfNotExist(BowVector& bow,WordId id, WordValue v)
376  {
377  BowVector::iterator vit = bow.lower_bound(id);
378 
379  if(vit == bow.end() || (bow.key_comp()(id, vit->first)))
380  {
381  bow.insert(vit, BowVector::value_type(id, v));
382  }
383 
384  }
385 
386  static void normalize(BowVector& bow,LNorm norm_type)
387  {
388  double norm = 0.0;
389  BowVector::iterator it;
390 
391  if(norm_type == L1)
392  {
393  for(it = bow.begin(); it != bow.end(); ++it)
394  norm += fabs(it->second);
395  }
396  else
397  {
398  for(it = bow.begin(); it != bow.end(); ++it)
399  norm += it->second * it->second;
400  norm = sqrt(norm);
401  }
402 
403  if(norm > 0.0)
404  {
405  for(it = bow.begin(); it != bow.end(); ++it)
406  it->second /= norm;
407  }
408  }
409 
410  static void addFeature(FeatureVector& fvec,NodeId id, unsigned int i_feature)
411  {
412  FeatureVector::iterator vit = fvec.lower_bound(id);
413 
414  if(vit != fvec.end() && vit->first == id)
415  {
416  vit->second.push_back(i_feature);
417  }
418  else
419  {
420  vit = fvec.insert(vit, FeatureVector::value_type(id,
421  std::vector<unsigned int>() ));
422  vit->second.push_back(i_feature);
423  }
424  }
425 
426  /**
427  * Creates a level in the tree, under the parent, by running kmeans with
428  * a descriptor set, and recursively creates the subsequent levels too
429  * @param parent_id id of parent node
430  * @param descriptors descriptors to run the kmeans on
431  * @param current_level current level in the tree
432  */
433  void HKmeansStep(NodeId parent_id, const std::vector<TinyMat> &descriptors,
434  int current_level);
435 
436 // void unifyDescriptors()
437 // {
438 // if(!m_nodeDescriptors.empty()) return ;
439 // if(!m_nodes.size()) return;
440 // int featureBytes=0;
441 // for(int i=0;i<m_nodes.size();i++)
442 // {
443 // Node& node=m_nodes[i];
444 // if(node.descriptor.empty()) continue;
445 // if(m_nodeDescriptors.empty())
446 // {
447 // m_nodeDescriptors=TinyMat(m_nodes.size(),node.descriptor.cols,
448 // node.descriptor.type());
449 // featureBytes=m_nodeDescriptors.elemSize()*m_nodeDescriptors.cols;
450 // }
451 // uchar* dest=m_nodeDescriptors.data+featureBytes*i;
452 // memcpy(dest,node.descriptor.data,featureBytes);
453 // node.descriptor=TinyMat(1,node.descriptor.cols,node.descriptor.type(),dest);
454 // }
455 // }
456 
457  /// Tree node
458  struct Node
459  {
460  uint32_t childNum;
461  WordValue weight;
462 
463  /**
464  * Empty constructor
465  */
466  Node(): childNum(0), weight(0){}
467 
468  /**
469  * Returns whether the node is a leaf node
470  * @return true iff the node is a leaf
471  */
472  inline bool isLeaf() const { return childNum==0; }
473 
474  void addChild(NodeId _id)
475  {
476  childNum++;
477  }
478  };
479 
480 
482  {
483  typedef std::function<float(const uchar*,const uchar*)> DistanceFunc;
484 
485  static float hamming32(const uchar* a,const uchar* b)
486  {
487  const uint64_t *pa = (uint64_t*)a;
488  const uint64_t *pb = (uint64_t*)b;
489  return std::bitset<64>(pa[0]^pb[0]).count()+std::bitset<64>(pa[1]^pb[1]).count()
490  +std::bitset<64>(pa[2]^pb[2]).count()+std::bitset<64>(pa[3]^pb[3]).count();
491  }
492 
493  static float hamming64(const uchar* a,const uchar* b){
494  const uint64_t *pa = (uint64_t*)a;
495  const uint64_t *pb = (uint64_t*)b;
496  return std::bitset<64>(pa[0]^pb[0]).count()+std::bitset<64>(pa[1]^pb[1]).count()
497  +std::bitset<64>(pa[2]^pb[2]).count()+std::bitset<64>(pa[3]^pb[3]).count()
498  +std::bitset<64>(pa[4]^pb[4]).count()+std::bitset<64>(pa[5]^pb[5]).count()
499  +std::bitset<64>(pa[6]^pb[6]).count()+std::bitset<64>(pa[7]^pb[7]).count();
500  }
501 
502  static float hamming8x(const uchar* a,const uchar* b,int bytes)
503  {
504  const uint64_t *pa = (uint64_t*)a;
505  const uint64_t *pb = (uint64_t*)b;
506 
507  uint64_t ret = 0;
508  for(size_t i = 0; i < bytes / sizeof(uint64_t); ++i, ++pa, ++pb)
509  {
510  ret+=std::bitset<64>(*pa ^ *pb).count();
511  }
512  return ret;
513  }
514 
515 #if defined(__AVX2__)
516  static float l2avx(const uchar* a,const uchar* b,int floats){
517  int _nwords=floats/8;
518  __m256 sum=_mm256_setzero_ps(), sub_mult;
519  __m256* aptr=(__m256*)a;
520  __m256* bptr=(__m256*)b;
521  for(int i=0;i<_nwords;i++){
522  sub_mult=_mm256_sub_ps(aptr[i],bptr[i]);
523  sub_mult=_mm256_mul_ps(sub_mult,sub_mult);
524  sum=_mm256_add_ps(sum,sub_mult);
525  }
526  sum=_mm256_hadd_ps(sum,sum);
527  sum=_mm256_hadd_ps(sum,sum);
528  float *sum_ptr=(float*)&sum;
529  return sum_ptr[0]+sum_ptr[4];
530  }
531 #endif
532 
533 #if defined(__SSE2__)&&defined(__SSE__)
534  static float l2sse(const uchar* a,const uchar* b,int floats){
535  int _nwords=floats/4;
536  __m128 sum=_mm_setzero_ps(), sub_mult;
537  float* sum_ptr=(float*)&sum;
538  //substract, multiply and accumulate
539  __m128* aptr=(__m128*)a;
540  __m128* bptr=(__m128*)b;
541  for(int i=0;i<_nwords;i++){
542  sub_mult=_mm_sub_ps(aptr[i],bptr[i]);
543  sub_mult=_mm_mul_ps(sub_mult,sub_mult);
544  sum=_mm_add_ps(sum,sub_mult);
545  }
546  return sum_ptr[0]+sum_ptr[1]+sum_ptr[2]+sum_ptr[3] ;
547  }
548 #endif
549 
550  static float l2generic(const uchar* a,const uchar* b,int floats){
551  float sqd = 0.,tmp;
552  const float *a_ptr=(float*)a;
553  const float *b_ptr=(float*)b;
554  for(int i = 0; i < floats; i ++)
555  {
556  tmp = (a_ptr[i ] - b_ptr[i ]);
557  sqd += tmp*tmp;
558  }
559  return sqd;
560  }
561 
562  static DistanceFunc create(const TinyMat& des){
563  //binary descriptor
564  if (des.type()==GImageType<uchar>::Type){
565  if(des.cols==32) return std::bind(&DistanceFactory::hamming32,std::placeholders::_1,std::placeholders::_2);
566  else if(des.cols==64) return std::bind(&DistanceFactory::hamming64,std::placeholders::_1,std::placeholders::_2);
567  else if(des.cols%8==0) return std::bind(&DistanceFactory::hamming8x,std::placeholders::_1,std::placeholders::_2,des.cols);
568  else return nullptr;
569  }
570  else if(des.type()==GImageType<float>::Type){
571 #if defined(__AVX2__)
572  if(des.cols%8) return std::bind(&DistanceFactory::l2avx,std::placeholders::_1,std::placeholders::_2,des.cols);
573 #endif
574 #if defined(__SSE2__)
575  if(des.cols%4) return std::bind(&DistanceFactory::l2sse,std::placeholders::_1,std::placeholders::_2,des.cols);
576 #endif
577  return std::bind(&DistanceFactory::l2generic,std::placeholders::_1,std::placeholders::_2,des.cols);
578  }
579  else return nullptr;
580  }
581 
582  };
583 public:
584  /// Branching factor
585  int m_k;
586 
587  /// Depth levels
588  int m_L;
589 
590  /// Weighting method
592 
593  /// Scoring method
595 
596  /// Object for computing scores
597  std::shared_ptr<GeneralScoring> m_scoring_object;
598 
599  /// Tree nodes
600  std::vector<Node> m_nodes;
601  TinyMat m_nodeDescriptors;
602 
603 private:
604  std::shared_ptr<msg::ThreadPool> m_workthreads;
605  DistanceFactory::DistanceFunc m_distanceFunc;
606 
607  /// Words of the vocabulary (tree leaves)
608  /// this condition holds: m_words[wid]->word_id == wid
609 // std::vector<Node*> m_words;
610 };
611 /// Base class of scoring functions
613 {
614 public:
615  /**
616  * Computes the score between two vectors. Vectors must be sorted and
617  * normalized if necessary
618  * @param v (in/out)
619  * @param w (in/out)
620  * @return score
621  */
622  virtual double score(const BowVector &v, const BowVector &w) const = 0;
623 
624  /**
625  * Returns whether a vector must be normalized before scoring according
626  * to the scoring scheme
627  * @param norm norm to use
628  * @return true iff must normalize
629  */
630  virtual bool mustNormalize(Vocabulary::LNorm &norm) const = 0;
631 
632  static inline const double LOG_EPS(){
633  static const double logeps=log(DBL_EPSILON);
634  return logeps;
635  }
636 
637  virtual ~GeneralScoring() {} //!< Required for virtual base classes
638 
639 };
640 
641 /**
642  * Macro for defining Scoring classes
643  * @param NAME name of class
644  * @param MUSTNORMALIZE if vectors must be normalized to compute the score
645  * @param NORM type of norm to use when MUSTNORMALIZE
646  */
647 #define __SCORING_CLASS(NAME, MUSTNORMALIZE, NORM) \
648  NAME: public GeneralScoring \
649  { public: \
650  /** \
651  * Computes score between two vectors \
652  * @param v \
653  * @param w \
654  * @return score between v and w \
655  */ \
656  virtual double score(const BowVector &v, const BowVector &w) const; \
657  \
658  /** \
659  * Says if a vector must be normalized according to the scoring function \
660  * @param norm (out) if true, norm to use
661  * @return true iff vectors must be normalized \
662  */ \
663  virtual inline bool mustNormalize(Vocabulary::LNorm &norm) const \
664  { norm = NORM; return MUSTNORMALIZE; } \
665  }
666 
667 /// L1 Scoring object
668 class __SCORING_CLASS(L1Scoring, true, Vocabulary::L1);
669 
670 /// L2 Scoring object
671 class __SCORING_CLASS(L2Scoring, true, Vocabulary::L2);
672 
673 /// Chi square Scoring object
674 class __SCORING_CLASS(ChiSquareScoring, true, Vocabulary::L1);
675 
676 /// KL divergence Scoring object
677 class __SCORING_CLASS(KLScoring, true, Vocabulary::L1);
678 
679 /// Bhattacharyya Scoring object
680 class __SCORING_CLASS(BhattacharyyaScoring, true, Vocabulary::L1);
681 
682 /// Dot product Scoring object
683 class __SCORING_CLASS(DotProductScoring, false, Vocabulary::L1);
684 
685 #undef __SCORING_CLASS
686 
687 
688 // ---------------------------------------------------------------------------
689 // ---------------------------------------------------------------------------
690 
691 inline double L1Scoring::score(const BowVector &v1, const BowVector &v2) const
692 {
693  BowVector::const_iterator v1_it, v2_it;
694  const BowVector::const_iterator v1_end = v1.end();
695  const BowVector::const_iterator v2_end = v2.end();
696 
697  v1_it = v1.begin();
698  v2_it = v2.begin();
699 
700  double score = 0;
701 
702  while(v1_it != v1_end && v2_it != v2_end)
703  {
704  const WordValue& vi = v1_it->second;
705  const WordValue& wi = v2_it->second;
706 
707  if(v1_it->first == v2_it->first)
708  {
709  score += fabs(vi - wi) - fabs(vi) - fabs(wi);
710 
711  // move v1 and v2 forward
712  ++v1_it;
713  ++v2_it;
714  }
715  else if(v1_it->first < v2_it->first)
716  {
717  // move v1 forward
718  v1_it = v1.lower_bound(v2_it->first);
719  // v1_it = (first element >= v2_it.id)
720  }
721  else
722  {
723  // move v2 forward
724  v2_it = v2.lower_bound(v1_it->first);
725  // v2_it = (first element >= v1_it.id)
726  }
727  }
728 
729  // ||v - w||_{L1} = 2 + Sum(|v_i - w_i| - |v_i| - |w_i|)
730  // for all i | v_i != 0 and w_i != 0
731  // (Nister, 2006)
732  // scaled_||v - w||_{L1} = 1 - 0.5 * ||v - w||_{L1}
733  score = -score/2.0;
734 
735  return score; // [0..1]
736 }
737 
738 // ---------------------------------------------------------------------------
739 // ---------------------------------------------------------------------------
740 
741 inline double L2Scoring::score(const BowVector &v1, const BowVector &v2) const
742 {
743  BowVector::const_iterator v1_it, v2_it;
744  const BowVector::const_iterator v1_end = v1.end();
745  const BowVector::const_iterator v2_end = v2.end();
746 
747  v1_it = v1.begin();
748  v2_it = v2.begin();
749 
750  double score = 0;
751 
752  while(v1_it != v1_end && v2_it != v2_end)
753  {
754  const WordValue& vi = v1_it->second;
755  const WordValue& wi = v2_it->second;
756 
757  if(v1_it->first == v2_it->first)
758  {
759  score += vi * wi;
760 
761  // move v1 and v2 forward
762  ++v1_it;
763  ++v2_it;
764  }
765  else if(v1_it->first < v2_it->first)
766  {
767  // move v1 forward
768  v1_it = v1.lower_bound(v2_it->first);
769  // v1_it = (first element >= v2_it.id)
770  }
771  else
772  {
773  // move v2 forward
774  v2_it = v2.lower_bound(v1_it->first);
775  // v2_it = (first element >= v1_it.id)
776  }
777  }
778 
779  // ||v - w||_{L2} = sqrt( 2 - 2 * Sum(v_i * w_i) )
780  // for all i | v_i != 0 and w_i != 0 )
781  // (Nister, 2006)
782  if(score >= 1) // rounding errors
783  score = 1.0;
784  else
785  score = 1.0 - sqrt(1.0 - score); // [0..1]
786 
787  return score;
788 }
789 
790 // ---------------------------------------------------------------------------
791 // ---------------------------------------------------------------------------
792 
793 inline double ChiSquareScoring::score(const BowVector &v1, const BowVector &v2)
794  const
795 {
796  BowVector::const_iterator v1_it, v2_it;
797  const BowVector::const_iterator v1_end = v1.end();
798  const BowVector::const_iterator v2_end = v2.end();
799 
800  v1_it = v1.begin();
801  v2_it = v2.begin();
802 
803  double score = 0;
804 
805  // all the items are taken into account
806 
807  while(v1_it != v1_end && v2_it != v2_end)
808  {
809  const WordValue& vi = v1_it->second;
810  const WordValue& wi = v2_it->second;
811 
812  if(v1_it->first == v2_it->first)
813  {
814  // (v-w)^2/(v+w) - v - w = -4 vw/(v+w)
815  // we move the -4 out
816  if(vi + wi != 0.0) score += vi * wi / (vi + wi);
817 
818  // move v1 and v2 forward
819  ++v1_it;
820  ++v2_it;
821  }
822  else if(v1_it->first < v2_it->first)
823  {
824  // move v1 forward
825  v1_it = v1.lower_bound(v2_it->first);
826  }
827  else
828  {
829  // move v2 forward
830  v2_it = v2.lower_bound(v1_it->first);
831  }
832  }
833 
834  // this takes the -4 into account
835  score = 2. * score; // [0..1]
836 
837  return score;
838 }
839 
840 // ---------------------------------------------------------------------------
841 // ---------------------------------------------------------------------------
842 
843 inline double KLScoring::score(const BowVector &v1, const BowVector &v2) const
844 {
845  BowVector::const_iterator v1_it, v2_it;
846  const BowVector::const_iterator v1_end = v1.end();
847  const BowVector::const_iterator v2_end = v2.end();
848 
849  v1_it = v1.begin();
850  v2_it = v2.begin();
851 
852  double score = 0;
853 
854  // all the items or v are taken into account
855 
856  while(v1_it != v1_end && v2_it != v2_end)
857  {
858  const WordValue& vi = v1_it->second;
859  const WordValue& wi = v2_it->second;
860 
861  if(v1_it->first == v2_it->first)
862  {
863  if(vi != 0 && wi != 0) score += vi * log(vi/wi);
864 
865  // move v1 and v2 forward
866  ++v1_it;
867  ++v2_it;
868  }
869  else if(v1_it->first < v2_it->first)
870  {
871  // move v1 forward
872  score += vi * (log(vi) - LOG_EPS());
873  ++v1_it;
874  }
875  else
876  {
877  // move v2_it forward, do not add any score
878  v2_it = v2.lower_bound(v1_it->first);
879  // v2_it = (first element >= v1_it.id)
880  }
881  }
882 
883  // sum rest of items of v
884  for(; v1_it != v1_end; ++v1_it)
885  if(v1_it->second != 0)
886  score += v1_it->second * (log(v1_it->second) - LOG_EPS());
887 
888  return score; // cannot be scaled
889 }
890 
891 // ---------------------------------------------------------------------------
892 // ---------------------------------------------------------------------------
893 
894 inline double BhattacharyyaScoring::score(const BowVector &v1,
895  const BowVector &v2) const
896 {
897  BowVector::const_iterator v1_it, v2_it;
898  const BowVector::const_iterator v1_end = v1.end();
899  const BowVector::const_iterator v2_end = v2.end();
900 
901  v1_it = v1.begin();
902  v2_it = v2.begin();
903 
904  double score = 0;
905 
906  while(v1_it != v1_end && v2_it != v2_end)
907  {
908  const WordValue& vi = v1_it->second;
909  const WordValue& wi = v2_it->second;
910 
911  if(v1_it->first == v2_it->first)
912  {
913  score += sqrt(vi * wi);
914 
915  // move v1 and v2 forward
916  ++v1_it;
917  ++v2_it;
918  }
919  else if(v1_it->first < v2_it->first)
920  {
921  // move v1 forward
922  v1_it = v1.lower_bound(v2_it->first);
923  // v1_it = (first element >= v2_it.id)
924  }
925  else
926  {
927  // move v2 forward
928  v2_it = v2.lower_bound(v1_it->first);
929  // v2_it = (first element >= v1_it.id)
930  }
931  }
932 
933  return score; // already scaled
934 }
935 
936 // ---------------------------------------------------------------------------
937 // ---------------------------------------------------------------------------
938 
939 inline double DotProductScoring::score(const BowVector &v1,
940  const BowVector &v2) const
941 {
942  BowVector::const_iterator v1_it, v2_it;
943  const BowVector::const_iterator v1_end = v1.end();
944  const BowVector::const_iterator v2_end = v2.end();
945 
946  v1_it = v1.begin();
947  v2_it = v2.begin();
948 
949  double score = 0;
950 
951  while(v1_it != v1_end && v2_it != v2_end)
952  {
953  const WordValue& vi = v1_it->second;
954  const WordValue& wi = v2_it->second;
955 
956  if(v1_it->first == v2_it->first)
957  {
958  score += vi * wi;
959 
960  // move v1 and v2 forward
961  ++v1_it;
962  ++v2_it;
963  }
964  else if(v1_it->first < v2_it->first)
965  {
966  // move v1 forward
967  v1_it = v1.lower_bound(v2_it->first);
968  // v1_it = (first element >= v2_it.id)
969  }
970  else
971  {
972  // move v2 forward
973  v2_it = v2.lower_bound(v1_it->first);
974  // v2_it = (first element >= v1_it.id)
975  }
976  }
977 
978  return score; // cannot scale
979 }
980 
982  (int k, int L, WeightingType weighting, ScoringType scoring)
983  : m_k(k), m_L(L), m_weighting(weighting), m_scoring(scoring),m_distanceFunc(nullptr)
984 {
986 }
987 
988 // --------------------------------------------------------------------------
989 
990 
992  (const std::string &filename): m_scoring_object(NULL),m_distanceFunc(nullptr)
993 {
994  load(filename);
995 }
996 
997 // --------------------------------------------------------------------------
998 
999 
1001 {
1002  m_scoring_object.reset();
1003 
1004  switch(m_scoring)
1005  {
1006  case L1_NORM:
1007  m_scoring_object = std::shared_ptr<GeneralScoring>(new L1Scoring);
1008  break;
1009 
1010  case L2_NORM:
1011  m_scoring_object = std::shared_ptr<GeneralScoring>(new L2Scoring);
1012  break;
1013 
1014  case CHI_SQUARE:
1015  m_scoring_object = std::shared_ptr<GeneralScoring>(new ChiSquareScoring);
1016  break;
1017 
1018  case KL:
1019  m_scoring_object = std::shared_ptr<GeneralScoring>(new KLScoring);
1020  break;
1021 
1022  case BHATTACHARYYA:
1023  m_scoring_object = std::shared_ptr<GeneralScoring>(new BhattacharyyaScoring);
1024  break;
1025 
1026  case DOT_PRODUCT:
1027  m_scoring_object = std::shared_ptr<GeneralScoring>(new DotProductScoring);
1028  break;
1029 
1030  }
1031 }
1032 
1033 // --------------------------------------------------------------------------
1034 
1035 
1037 {
1038  m_scoring = type;
1040 }
1041 
1042 // --------------------------------------------------------------------------
1043 
1044 
1046 {
1047  this->m_weighting = type;
1048 }
1049 
1050 inline std::shared_ptr<Vocabulary> Vocabulary::create(const std::vector<TinyMat> &imgFeatures,
1051  int k , int L,WeightingType weighting , ScoringType scoring)
1052 {
1053  std::shared_ptr<Vocabulary> voc(new Vocabulary(k,L,weighting,scoring));
1054 
1055  // expected_nodes = Sum_{i=0..L} ( k^i )
1056  int expected_nodes =(int)((pow((double)voc->m_k, (double)voc->m_L + 1) - 1)/(voc->m_k - 1));
1057  TinyMat firstF=imgFeatures.front();
1058  voc->m_nodes.resize(expected_nodes);
1059  voc->m_nodeDescriptors=TinyMat(expected_nodes,firstF.cols,firstF.type(),nullptr,false,32);
1060  int featBytes=firstF.elemSize()*firstF.cols;
1061 
1062  if(!voc->m_distanceFunc) voc->m_distanceFunc=DistanceFactory::create(firstF);
1063 
1064  // create the tree
1065  int featureNum=0;
1066  for(const TinyMat& m:imgFeatures)
1067  featureNum+=m.rows;
1068  TinyMat featureHolder(featureNum,firstF.cols,firstF.type(),nullptr,false,32);
1069  LOG_IF(INFO,(((int64_t)featureHolder.data)&0x1F)!=0)
1070  <<"FeatureHolder adress "<<((int64_t)featureHolder.data)
1071  <<" are not ready to perform AVX acceleration.\n";
1072  std::vector<TinyMat> vtf;
1073  vtf.reserve(featureNum);
1074  for(const TinyMat& m:imgFeatures){
1075  auto curSize=vtf.size();
1076  memcpy(featureHolder.data+featBytes*curSize,m.data,featBytes*m.rows);
1077  for(int iend=curSize+m.rows;curSize<iend;curSize++)
1078  vtf.push_back(featureHolder.row(curSize));
1079  }
1080 
1081  voc->m_workthreads=std::shared_ptr<msg::ThreadPool>(new msg::ThreadPool(k));
1082  voc->HKmeansStep(0, vtf, 1);
1083 
1084  // and set the weight of each node of the tree
1085  const unsigned int NWords = voc->m_nodes.size();
1086  const unsigned int NDocs = imgFeatures.size();
1087 
1088  if(voc->m_weighting == TF || voc->m_weighting == BINARY)
1089  {
1090  // idf part must be 1 always
1091  for(unsigned int i = 0; i < NWords; i++)
1092  voc->m_nodes[i].weight = 1;
1093  }
1094  else if(voc->m_weighting == IDF || voc->m_weighting == TF_IDF)
1095  {
1096  // IDF and TF-IDF: we calculte the idf path now
1097 
1098  // Note: this actually calculates the idf part of the tf-idf score.
1099  // The complete tf-idf score is calculated in ::transform
1100 
1101  std::vector<unsigned int> Ni(NWords, 0);
1102  std::vector<bool> counted(NWords, false);
1103 
1104 
1105  for(const TinyMat& mit:imgFeatures)
1106  {
1107  fill(counted.begin(), counted.end(), false);
1108  for(int r=0;r<mit.rows;r++)
1109  {
1110  WordId word_id;
1111  WordValue weight;
1112  voc->transform(mit.row(r), word_id,weight);
1113 
1114  if(!counted[word_id])
1115  {
1116  Ni[word_id]++;
1117  counted[word_id] = true;
1118  }
1119  }
1120  }
1121 
1122  // set ln(N/Ni)
1123  for(unsigned int i = 0; i < NWords; i++)
1124  {
1125  if(Ni[i] > 0)
1126  {
1127  voc->m_nodes[i].weight = log((double)NDocs / (double)Ni[i]);
1128  }// else // This cannot occur if using kmeans++
1129  }
1130  }
1131 
1132 // voc->unifyDescriptors();
1133  return voc;
1134 
1135 }
1136 // --------------------------------------------------------------------------
1137 
1138 inline void Vocabulary::HKmeansStep(NodeId parent_id,
1139  const std::vector<TinyMat> &descriptors, int current_level)
1140 {
1141 
1142  if(descriptors.empty()) return;
1143  // features associated to each cluster
1144  std::vector<TinyMat> clusters;
1145  std::vector<std::vector<unsigned int> > groups; // groups[i] = [j1, j2, ...]
1146  // j1, j2, ... indices of descriptors associated to cluster i
1147 
1148  clusters.reserve(m_k);
1149  groups.reserve(m_k);
1150 
1151 
1152  if((int)descriptors.size() <= m_k)
1153  {
1154  // trivial case: one cluster per feature
1155  groups.resize(descriptors.size());
1156 
1157  for(unsigned int i = 0; i < descriptors.size(); i++)
1158  {
1159  groups[i].push_back(i);
1160  clusters.push_back(descriptors[i]);
1161  }
1162  }
1163  else
1164  {
1165  // select clusters and groups with kmeans
1166 
1167  bool first_time = true;
1168  bool goon = true;
1169 
1170  // to check if clusters move after iterations
1171  std::vector<int> last_association, current_association;
1172 
1173  while(goon)
1174  {
1175  // 1. Calculate clusters
1176 
1177  if(first_time)
1178  {
1179  // random sample
1180 // initiateClusters(descriptors, clusters);
1181  // Implements kmeans++ seeding algorithm
1182  // Algorithm:
1183  // 1. Choose one center uniformly at random from among the data points.
1184  // 2. For each data point x, compute D(x), the distance between x and the nearest
1185  // center that has already been chosen.
1186  // 3. Add one new data point as a center. Each point x is chosen with probability
1187  // proportional to D(x)^2.
1188  // 4. Repeat Steps 2 and 3 until k centers have been chosen.
1189  // 5. Now that the initial centers have been chosen, proceed using standard k-means
1190  // clustering.
1191 
1192 
1193  // DUtils::Random::SeedRandOnce();
1194 
1195  clusters.resize(0);
1196  clusters.reserve(m_k);
1197  std::vector<double> min_dists(descriptors.size(), std::numeric_limits<double>::max());
1198 
1199  // 1.
1200 
1201  int ifeature = rand()% descriptors.size();//DUtils::Random::RandomInt(0, pfeatures.size()-1);
1202 
1203  // create first cluster
1204  clusters.push_back(descriptors[ifeature]);
1205 
1206  // compute the initial distances
1207  std::vector<double>::iterator dit;
1208  dit = min_dists.begin();
1209  for(auto fit = descriptors.begin(); fit != descriptors.end(); ++fit, ++dit)
1210  {
1211  *dit = distance((*fit), clusters.back());
1212  }
1213 
1214  while((int)clusters.size() < m_k)
1215  {
1216  // 2.
1217  dit = min_dists.begin();
1218  for(auto fit = descriptors.begin(); fit != descriptors.end(); ++fit, ++dit)
1219  {
1220  if(*dit > 0)
1221  {
1222  double dist = distance((*fit), clusters.back());
1223  if(dist < *dit) *dit = dist;
1224  }
1225  }
1226 
1227  // 3.
1228  double dist_sum = std::accumulate(min_dists.begin(), min_dists.end(), 0.0);
1229 
1230  if(dist_sum > 0)
1231  {
1232  double cut_d;
1233  do
1234  {
1235 
1236  cut_d = (double(rand())/ double(RAND_MAX))* dist_sum;
1237  } while(cut_d == 0.0);
1238 
1239  double d_up_now = 0;
1240  for(dit = min_dists.begin(); dit != min_dists.end(); ++dit)
1241  {
1242  d_up_now += *dit;
1243  if(d_up_now >= cut_d) break;
1244  }
1245 
1246  if(dit == min_dists.end())
1247  ifeature = descriptors.size()-1;
1248  else
1249  ifeature = dit - min_dists.begin();
1250 
1251 
1252  clusters.push_back(descriptors[ifeature]);
1253  } // if dist_sum > 0
1254  else
1255  break;
1256 
1257  } // while(used_clusters < m_k)
1258  }
1259  else
1260  {
1261  // calculate cluster centres
1262 
1263  for(unsigned int c = 0; c < clusters.size(); ++c)
1264  {
1265  std::vector<TinyMat> cluster_descriptors;
1266  cluster_descriptors.reserve(groups[c].size());
1267  std::vector<unsigned int>::const_iterator vit;
1268  for(vit = groups[c].begin(); vit != groups[c].end(); ++vit)
1269  {
1270  cluster_descriptors.push_back(descriptors[*vit]);
1271  }
1272 
1273  meanValue(cluster_descriptors, clusters[c]);
1274  }
1275 
1276  } // if(!first_time)
1277 
1278  // 2. Associate features with clusters
1279 
1280  // calculate distances to cluster centers
1281  groups.clear();
1282  groups.resize(clusters.size(), std::vector<unsigned int>());
1283  current_association.resize(descriptors.size());
1284 
1285  //assoc.clear();
1286 
1287  //unsigned int d = 0;
1288  for(auto fit = descriptors.begin(); fit != descriptors.end(); ++fit)//, ++d)
1289  {
1290  double best_dist = distance((*fit), clusters[0]);
1291  unsigned int icluster = 0;
1292 
1293  for(unsigned int c = 1; c < clusters.size(); ++c)
1294  {
1295  double dist = distance((*fit), clusters[c]);
1296  if(dist < best_dist)
1297  {
1298  best_dist = dist;
1299  icluster = c;
1300  }
1301  }
1302 
1303  //assoc.ref<unsigned char>(icluster, d) = 1;
1304 
1305  groups[icluster].push_back(fit - descriptors.begin());
1306  current_association[ fit - descriptors.begin() ] = icluster;
1307  }
1308 
1309  // kmeans++ ensures all the clusters has any feature associated with them
1310 
1311  // 3. check convergence
1312  if(first_time)
1313  {
1314  first_time = false;
1315  }
1316  else
1317  {
1318  //goon = !eqUChar(last_assoc, assoc);
1319 
1320  goon = false;
1321  for(unsigned int i = 0; i < current_association.size(); i++)
1322  {
1323  if(current_association[i] != last_association[i]){
1324  goon = true;
1325  break;
1326  }
1327  }
1328  }
1329 
1330  if(goon)
1331  {
1332  // copy last feature-cluster association
1333  last_association = current_association;
1334  //last_assoc = assoc.clone();
1335  }
1336 
1337  } // while(goon)
1338 
1339  } // if must run kmeans
1340 
1341  // create nodes
1342  Node& parent=m_nodes[parent_id];
1343  parent.childNum=clusters.size();
1344  int des_bytes=m_nodeDescriptors.cols*m_nodeDescriptors.elemSize();
1345  int childID=parent_id*m_k+1;
1346  uchar* des_data=m_nodeDescriptors.data+des_bytes*(childID);
1347  for(unsigned int i = 0; i < clusters.size(); ++i)
1348  {
1349  memcpy(des_data,clusters[i].data,des_bytes);
1350  }
1351 
1352  std::vector<std::future<void> > futures;
1353  // go on with the next level
1354  if(current_level < m_L)
1355  {
1356  // iterate again with the resulting clusters
1357  for(unsigned int i = 0; i < parent.childNum; ++i)
1358  {
1359  NodeId id = childID+i;
1360 
1361  std::vector<TinyMat> child_features;
1362  child_features.reserve(groups[i].size());
1363 
1364  std::vector<unsigned int>::const_iterator vit;
1365  for(vit = groups[i].begin(); vit != groups[i].end(); ++vit)
1366  {
1367  child_features.push_back(descriptors[*vit]);
1368  }
1369 
1370  if(child_features.size() > 1)
1371  {
1372  if(m_workthreads&&current_level==1)
1373  futures.push_back(m_workthreads->Add([this,id,child_features,current_level](){
1374  return HKmeansStep(id, child_features, current_level + 1);
1375  }));
1376  else
1377  HKmeansStep(id, child_features, current_level + 1);
1378  }
1379  }
1380  }
1381  for(auto& f:futures) f.wait();
1382 
1383 }
1384 
1385 
1386 // --------------------------------------------------------------------------
1387 
1389 {
1390  return 0;
1391 // long sum = 0;
1392 // for(auto wit = m_words.begin(); wit != m_words.end(); ++wit)
1393 // {
1394 // const Node *p = *wit;
1395 
1396 // for(; p->id != 0; sum++) p = &m_nodes[p->parent];
1397 // }
1398 
1399 // return (float)((double)sum / (double)m_words.size());
1400 }
1401 
1402 // --------------------------------------------------------------------------
1403 
1404 
1405 inline TinyMat Vocabulary::getWord(WordId wid) const
1406 {
1407  return m_nodeDescriptors.row(wid);
1408 }
1409 
1410 // --------------------------------------------------------------------------
1411 
1412 
1413 inline WordValue Vocabulary::getWordWeight(WordId wid) const
1414 {
1415  return m_nodes[wid].weight;
1416 }
1417 
1418 // --------------------------------------------------------------------------
1419 
1420 
1421 inline WordId Vocabulary::transform
1422  (const TinyMat& feature) const
1423 {
1424  if(empty())
1425  {
1426  return 0;
1427  }
1428 
1429  WordId wid;
1430  WordValue weight;
1431  transform(feature, wid,weight);
1432  return wid;
1433 }
1434 
1435 // --------------------------------------------------------------------------
1436 
1437 inline void Vocabulary::transform(
1438  const TinyMat& features, BowVector &v) const
1439 {
1440  // std::vector<TinyMat> vf(features.rows);
1441  // for(int r=0;r<features.rows;r++) vf[r]=features.rowRange(r,r+1);
1442  // transform(vf,v);
1443 
1444 
1445 
1446  v.clear();
1447 
1448  if(empty())
1449  {
1450  return;
1451  }
1452 
1453  // normalize
1454  LNorm norm;
1455  bool must = m_scoring_object->mustNormalize(norm);
1456 
1457 
1458  if(m_weighting == TF || m_weighting == TF_IDF)
1459  {
1460  for(int r=0;r<features.rows;r++)
1461  {
1462  WordId id;
1463  WordValue w;
1464  // w is the idf value if TF_IDF, 1 if TF
1465  transform(features.row(r), id, w);
1466  // not stopped
1467  if(w > 0) addWeight(v,id, w);
1468  }
1469 
1470  if(!v.empty() && !must)
1471  {
1472  // unnecessary when normalizing
1473  const double nd = v.size();
1474  for(BowVector::iterator vit = v.begin(); vit != v.end(); vit++)
1475  vit->second /= nd;
1476  }
1477 
1478  }
1479  else // IDF || BINARY
1480  {
1481  for(int r=0;r<features.rows;r++)
1482  {
1483  WordId id;
1484  WordValue w;
1485  // w is idf if IDF, or 1 if BINARY
1486 
1487  transform(features.row(r), id, w);
1488 
1489  // not stopped
1490  if(w > 0) addIfNotExist(v,id, w);
1491 
1492  } // if add_features
1493  } // if m_weighting == ...
1494 
1495  if(must) normalize(v,norm);
1496 
1497 }
1498 
1499 
1500 
1501 inline void Vocabulary::transform(const std::vector<TinyMat>& features, BowVector &v) const
1502 {
1503  v.clear();
1504 
1505  if(empty())
1506  {
1507  return;
1508  }
1509 
1510  // normalize
1511  LNorm norm;
1512  bool must = m_scoring_object->mustNormalize(norm);
1513 
1514 
1515  if(m_weighting == TF || m_weighting == TF_IDF)
1516  {
1517  for(auto fit = features.begin(); fit < features.end(); ++fit)
1518  {
1519  WordId id;
1520  WordValue w;
1521  // w is the idf value if TF_IDF, 1 if TF
1522 
1523  transform(*fit, id, w);
1524 
1525  // not stopped
1526  if(w > 0) addWeight(v,id, w);
1527  }
1528 
1529  if(!v.empty() && !must)
1530  {
1531  // unnecessary when normalizing
1532  const double nd = v.size();
1533  for(BowVector::iterator vit = v.begin(); vit != v.end(); vit++)
1534  vit->second /= nd;
1535  }
1536 
1537  }
1538  else // IDF || BINARY
1539  {
1540  for(auto fit = features.begin(); fit < features.end(); ++fit)
1541  {
1542  WordId id;
1543  WordValue w;
1544  // w is idf if IDF, or 1 if BINARY
1545 
1546  transform(*fit, id, w);
1547 
1548  // not stopped
1549  if(w > 0) addIfNotExist(v,id, w);
1550 
1551  } // if add_features
1552  } // if m_weighting == ...
1553 
1554  if(must) normalize(v,norm);
1555 }
1556 
1557 // --------------------------------------------------------------------------
1558 inline void Vocabulary::transform(const TinyMat& features,
1559  BowVector &v, FeatureVector &fv, int levelsup) const
1560 {
1561  v.clear();
1562  fv.clear();
1563 
1564  if(empty()) // safe for subclasses
1565  {
1566  return;
1567  }
1568 
1569  // normalize
1570  LNorm norm;
1571  bool must = m_scoring_object->mustNormalize(norm);
1572 
1573 
1574  if(m_weighting == TF || m_weighting == TF_IDF)
1575  {
1576  for(unsigned int i_feature = 0; i_feature<features.rows; ++i_feature)
1577  {
1578  WordId id;
1579  NodeId nid;
1580  WordValue w;
1581  // w is the idf value if TF_IDF, 1 if TF
1582 
1583  transform(features.row(i_feature), id, w, &nid, levelsup);
1584 
1585  if(w > 0) // not stopped
1586  {
1587  addWeight(v,id, w);
1588  addFeature(fv,nid, i_feature);
1589  }
1590  }
1591 
1592  if(!v.empty() && !must)
1593  {
1594  // unnecessary when normalizing
1595  const double nd = v.size();
1596  for(BowVector::iterator vit = v.begin(); vit != v.end(); vit++)
1597  vit->second /= nd;
1598  }
1599 
1600  }
1601  else // IDF || BINARY
1602  {
1603  for(unsigned int i_feature = 0; i_feature<features.rows; ++i_feature)
1604  {
1605  WordId id;
1606  NodeId nid;
1607  WordValue w;
1608  // w is idf if IDF, or 1 if BINARY
1609 
1610  transform(features.row(i_feature), id, w, &nid, levelsup);
1611 
1612  if(w > 0) // not stopped
1613  {
1614  addIfNotExist(v,id, w);
1615  addFeature(fv,nid, i_feature);
1616  }
1617  }
1618  } // if m_weighting == ...
1619 
1620  if(must) normalize(v,norm);
1621 }
1622 
1623 inline void Vocabulary::transform(
1624  const std::vector<TinyMat>& features,
1625  BowVector &v, FeatureVector &fv, int levelsup) const
1626 {
1627  v.clear();
1628  fv.clear();
1629 
1630  if(empty()) // safe for subclasses
1631  {
1632  return;
1633  }
1634 
1635  // normalize
1636  LNorm norm;
1637  bool must = m_scoring_object->mustNormalize(norm);
1638 
1639 
1640  if(m_weighting == TF || m_weighting == TF_IDF)
1641  {
1642  unsigned int i_feature = 0;
1643  for(auto fit = features.begin(); fit < features.end(); ++fit, ++i_feature)
1644  {
1645  WordId id;
1646  NodeId nid;
1647  WordValue w;
1648  // w is the idf value if TF_IDF, 1 if TF
1649 
1650  transform(*fit, id, w, &nid, levelsup);
1651 
1652  if(w > 0) // not stopped
1653  {
1654  addWeight(v,id, w);
1655  addFeature(fv,nid, i_feature);
1656  }
1657  }
1658 
1659  if(!v.empty() && !must)
1660  {
1661  // unnecessary when normalizing
1662  const double nd = v.size();
1663  for(BowVector::iterator vit = v.begin(); vit != v.end(); vit++)
1664  vit->second /= nd;
1665  }
1666 
1667  }
1668  else // IDF || BINARY
1669  {
1670  unsigned int i_feature = 0;
1671  for(auto fit = features.begin(); fit < features.end(); ++fit, ++i_feature)
1672  {
1673  WordId id;
1674  NodeId nid;
1675  WordValue w;
1676  // w is idf if IDF, or 1 if BINARY
1677 
1678  transform(*fit, id, w, &nid, levelsup);
1679 
1680  if(w > 0) // not stopped
1681  {
1682  addIfNotExist(v,id, w);
1683  addFeature(fv,nid, i_feature);
1684  }
1685  }
1686  } // if m_weighting == ...
1687 
1688  if(must) normalize(v,norm);
1689 }
1690 
1691 
1692 // --------------------------------------------------------------------------
1693 
1694 
1695 inline void Vocabulary::transform(const TinyMat &feature,
1696  WordId &word_id, WordValue &weight, NodeId *nid, int levelsup) const
1697 {
1698  // propagate the feature down the tree
1699 
1700 
1701  // level at which the node must be stored in nid, if given
1702  const int nid_level = m_L - levelsup;
1703  if(nid_level <= 0 && nid != NULL) *nid = 0; // root
1704 
1705  NodeId final_id = 0; // root
1706  int current_level = 0;
1707  int featureBytes=m_nodeDescriptors.cols*m_nodeDescriptors.elemSize();
1708 
1709  do
1710  {
1711  ++current_level;
1712  float best_d = std::numeric_limits<float>::max();
1713  NodeId best_id= final_id;
1714 
1715  const Node& node=m_nodes[final_id];
1716  NodeId id=final_id*m_k+1;
1717  for(NodeId idEnd=id+node.childNum;id<idEnd;++id)
1718  {
1719  float d = m_distanceFunc(feature.data, m_nodeDescriptors.data+featureBytes*id);
1720  if(d < best_d)
1721  {
1722  best_d = d;
1723  best_id = id;
1724  }
1725  }
1726  final_id=best_id;
1727 
1728  if(nid != NULL && current_level == nid_level)
1729  *nid = final_id;
1730 
1731  } while( !m_nodes[final_id].isLeaf() );
1732 
1733  // turn node id into word id
1734  word_id = final_id;
1735  weight = m_nodes[final_id].weight;
1736 }
1737 
1738 
1739 
1740 inline void Vocabulary::transform(const TinyMat &feature,
1741  WordId &word_id, WordValue &weight ) const
1742 {
1743  // propagate the feature down the tree
1744  // level at which the node must be stored in nid, if given
1745  NodeId final_id = 0;
1746  do
1747  {
1748  const Node& node = m_nodes[final_id];
1749  float best_d = std::numeric_limits<float>::max();
1750  NodeId bestID=final_id;
1751  int idx=0;
1752  for(int i=0;i<node.childNum;i++)
1753  {
1754  NodeId id=final_id*m_k+1+i;
1755  uint64_t dist= distance(feature, m_nodeDescriptors.row(id));
1756  if(dist < best_d)
1757  {
1758  best_d = dist;
1759  bestID = id;
1760  }
1761  idx++;
1762  }
1763  final_id=bestID;
1764 
1765  } while( !m_nodes[final_id].isLeaf() );
1766 
1767  // turn node id into word id
1768  word_id = final_id;//m_nodes[final_id].word_id;
1769  weight = m_nodes[final_id].weight;
1770 }
1771 // --------------------------------------------------------------------------
1772 
1773 inline NodeId Vocabulary::getParentNode
1774  (WordId wid, int levelsup) const
1775 {
1776  NodeId ret = wid;//m_words[wid]->id; // node id
1777  while(levelsup > 0 && ret != 0) // ret == 0 --> root
1778  {
1779  --levelsup;
1780  ret = (ret-1)/m_k;
1781  }
1782  return ret;
1783 }
1784 
1785 // --------------------------------------------------------------------------
1786 
1787 
1788 inline void Vocabulary::getWordsFromNode
1789  (NodeId nid, std::vector<WordId> &words) const
1790 {
1791  words.clear();
1792 
1793  if(m_nodes[nid].isLeaf())
1794  {
1795  words.push_back(nid);
1796  }
1797  else
1798  {
1799  words.reserve(m_k); // ^1, ^2, ...
1800 
1801  std::vector<NodeId> parents;
1802  parents.push_back(nid);
1803 
1804  while(!parents.empty())
1805  {
1806  NodeId parentid = parents.back();
1807  parents.pop_back();
1808 
1809  for(int i=0;i<m_nodes[parentid].childNum;i++)
1810  {
1811  const auto id=parentid*m_k+1+i;
1812 
1813  if(m_nodes[id].isLeaf())
1814  words.push_back(id);
1815  else
1816  parents.push_back(id);
1817 
1818  } // for each child
1819  } // while !parents.empty
1820  }
1821 }
1822 
1823 // --------------------------------------------------------------------------
1824 
1825 
1826 inline int Vocabulary::stopWords(double minWeight)
1827 {
1828  int c = 0;
1829  for(auto wit = m_nodes.begin(); wit != m_nodes.end(); ++wit)
1830  {
1831  if((*wit).weight < minWeight)
1832  {
1833  ++c;
1834  (*wit).weight = 0;
1835  }
1836  }
1837  return c;
1838 }
1839 
1840 // --------------------------------------------------------------------------
1841 
1842 
1843 inline bool Vocabulary::save(const std::string &filename, bool compressed) const
1844 {
1845  if(filename.find(".yml")!=std::string::npos)
1846  {
1847  return false;
1848  }
1849  compressed=false;
1850 
1851  std::ofstream out_str(filename);
1852  if (!out_str) throw std::runtime_error("Vocabulary::saveBinary Could not open file :"+filename+" for writing");
1853 
1854  uint64_t sig=88877711233;//magic number describing the file
1855  out_str.write((char*)&sig,sizeof(sig));
1856  out_str.write((char*)&compressed,sizeof(compressed));
1857  uint32_t nnodes=m_nodes.size();
1858  out_str.write((char*)&nnodes,sizeof(nnodes));
1859  if (nnodes==0) return false;
1860  //save everything to a stream
1861  std::stringstream aux_stream;
1862  aux_stream.write((char*)&m_k,sizeof(m_k));
1863  aux_stream.write((char*)&m_L,sizeof(m_L));
1864  aux_stream.write((char*)&m_scoring,sizeof(m_scoring));
1865  aux_stream.write((char*)&m_weighting,sizeof(m_weighting));
1866  int type=getDescritorType();
1867  int cols=getDescritorSize();
1868  int rows=1;
1869  aux_stream.write((char*)&cols,sizeof(cols));
1870  aux_stream.write((char*)&rows,sizeof(rows));
1871  aux_stream.write((char*)&type,sizeof(type));
1872 
1873  aux_stream.write((char*)m_nodes.data(),sizeof(Node)*m_nodes.size());
1874  aux_stream.write((char*)m_nodeDescriptors.data,
1875  m_nodeDescriptors.elemSize()*m_nodeDescriptors.total());
1876 
1877  //now, decide if compress or not
1878  if (compressed){
1879  return false;
1880  }
1881  else{
1882  out_str<<aux_stream.rdbuf();
1883  }
1884 
1885  return true;
1886 }
1887 
1888 // --------------------------------------------------------------------------
1889 
1890 inline bool Vocabulary::load(std::istream& ifile)
1891 {
1892  uint64_t sig;//magic number describing the file
1893  ifile.read((char*)&sig,sizeof(sig));
1894  if (sig==88877711233) {//it is a binary file. read from it
1895  bool compressed;
1896  uint32_t nnodes;
1897  ifile.read((char*)&compressed,sizeof(compressed));
1898  ifile.read((char*)&nnodes,sizeof(nnodes));
1899  if(nnodes==0) return false;
1900  std::istream *_used_str=0;
1901  if (compressed){
1902  return false;
1903  }
1904  else
1905  {
1906  _used_str=&ifile;
1907  }
1908 
1909  _used_str->read((char*)&m_k,sizeof(m_k));
1910  _used_str->read((char*)&m_L,sizeof(m_L));
1911  _used_str->read((char*)&m_scoring,sizeof(m_scoring));
1912  _used_str->read((char*)&m_weighting,sizeof(m_weighting));
1913 
1915 
1916  int type,cols,rows;
1917  _used_str->read((char*)&cols,sizeof(cols));
1918  _used_str->read((char*)&rows,sizeof(rows));
1919  _used_str->read((char*)&type,sizeof(type));
1920  m_nodes.resize(nnodes);
1921  m_nodeDescriptors=TinyMat(nnodes,cols,type,nullptr,false,32);
1922  m_distanceFunc=DistanceFactory::create(m_nodeDescriptors);
1923 
1924  _used_str->read((char*)m_nodes.data(),nnodes*sizeof(Node));
1925  _used_str->read((char*)m_nodeDescriptors.data,
1926  m_nodeDescriptors.total()*m_nodeDescriptors.elemSize());
1927 
1928  return true;
1929  }
1930  std::cerr<<"Vocabulary: Wrong signature.";
1931  return false;
1932 }
1933 
1934 inline bool Vocabulary::load(const std::string &filename)
1935 {
1936  // try binary formats : *.gbow
1937  {
1938  std::ifstream ifile(filename,std::ios::in|std::ios::binary);
1939  if (!ifile.is_open())
1940  {
1941  std::cerr<<"Vocabulary::load Could not open file "<<filename<<" for reading"<<std::endl;
1942  return false;
1943  }
1944  return load(ifile);
1945  }
1946 
1947  // other formats
1948  if(filename.find(".yml")!=std::string::npos)
1949  {
1950  throw std::runtime_error( "Vocabulary loading failure: .yml loading is not implemented!" );
1951  }
1952  else if(filename.find(".voc")!=std::string::npos)
1953  {
1954  throw std::runtime_error( "Vocabulary loading failure: .voc loading is not implemented!" );
1955  }
1956  return false;
1957 }
1958 // --------------------------------------------------------------------------
1959 
1960 /**
1961  * Writes printable information of the vocabulary
1962  * @param os stream to write to
1963  * @param voc
1964  */
1965 
1966 inline std::ostream& operator<<(std::ostream &os,
1967  const Vocabulary &voc)
1968 {
1969  os << "Vocabulary: k = " << voc.getBranchingFactor()
1970  << ", L = " << voc.getDepthLevels()
1971  << ", Weighting = ";
1972 
1973  switch(voc.getWeightingType())
1974  {
1975  case Vocabulary::TF_IDF: os << "tf-idf"; break;
1976  case Vocabulary::TF: os << "tf"; break;
1977  case Vocabulary::IDF: os << "idf"; break;
1978  case Vocabulary::BINARY: os << "binary"; break;
1979  }
1980 
1981  os << ", Scoring = ";
1982  switch(voc.getScoringType())
1983  {
1984  case Vocabulary::L1_NORM: os << "L1-norm"; break;
1985  case Vocabulary::L2_NORM: os << "L2-norm"; break;
1986  case Vocabulary::CHI_SQUARE: os << "Chi square distance"; break;
1987  case Vocabulary::KL: os << "KL-divergence"; break;
1988  case Vocabulary::BHATTACHARYYA: os << "Bhattacharyya coefficient"; break;
1989  case Vocabulary::DOT_PRODUCT: os << "Dot product"; break;
1990  }
1991 
1992  os << ", Number of words = " << voc.size();
1993 
1994  return os;
1995 }
1996 
1997 
1998 /**
1999  * @brief Vocabulary::clear
2000  */
2001 inline void Vocabulary::clear(){
2002  m_scoring_object.reset();
2003  m_scoring_object=0;
2004  m_nodes.clear();
2005  m_nodeDescriptors=TinyMat();
2006 }
2007 
2008 inline void Vocabulary::meanValue(const std::vector<TinyMat> &descriptors,
2009  TinyMat &mean)
2010 {
2011 
2012  if(descriptors.empty()) return;
2013 
2014  if(descriptors.size() == 1)
2015  {
2016  mean = descriptors[0].clone();
2017  return;
2018  }
2019  //binary descriptor
2020  if (descriptors[0].type()==GImageType<uchar>::Type ){
2021  //determine number of bytes of the binary descriptor
2022  int L= descriptors[0].cols;
2023  std::vector<int> sum( L * 8, 0);
2024 
2025  for(size_t i = 0; i < descriptors.size(); ++i)
2026  {
2027  const TinyMat &d = descriptors[i];
2028  const unsigned char *p = d.data;
2029 
2030  for(int j = 0; j < d.cols; ++j, ++p)
2031  {
2032  if(*p & (1 << 7)) ++sum[ j*8 ];
2033  if(*p & (1 << 6)) ++sum[ j*8 + 1 ];
2034  if(*p & (1 << 5)) ++sum[ j*8 + 2 ];
2035  if(*p & (1 << 4)) ++sum[ j*8 + 3 ];
2036  if(*p & (1 << 3)) ++sum[ j*8 + 4 ];
2037  if(*p & (1 << 2)) ++sum[ j*8 + 5 ];
2038  if(*p & (1 << 1)) ++sum[ j*8 + 6 ];
2039  if(*p & (1)) ++sum[ j*8 + 7 ];
2040  }
2041  }
2042 
2043  mean = TinyMat(1, L, GImageType<uchar>::Type);
2044  memset(mean.data,0,mean.total());
2045  unsigned char *p = mean.data;
2046 
2047  const int N2 = (int)descriptors.size() / 2 + descriptors.size() % 2;
2048  for(size_t i = 0; i < sum.size(); ++i)
2049  {
2050  if(sum[i] >= N2)
2051  {
2052  // set bit
2053  *p |= 1 << (7 - (i % 8));
2054  }
2055 
2056  if(i % 8 == 7) ++p;
2057  }
2058  }
2059  //non binary descriptor
2060  else{
2061  assert(descriptors[0].type()==GImageType<float>::Type);//ensure it is float
2062 
2063  mean= TinyMat(1, descriptors[0].cols,GImageType<float>::Type);
2064  memset(mean.data,0,mean.total()*mean.elemSize());
2065  float inv_s =1./double( descriptors.size());
2066  for(size_t i=0;i<descriptors.size();i++)
2067  for(int idx=0;idx<descriptors[i].total();idx++)
2068  mean.at<float>(idx) += ((float*)descriptors[i].data)[idx] * inv_s;
2069 
2070  }
2071 }
2072 
2073 inline float Vocabulary::distance(const TinyMat &a, const TinyMat &b)const
2074 {
2075  return m_distanceFunc(a.data,b.data);
2076 }
2077 
2078 }
2079 
2080 
2081 #endif
2082 
Definition: Vocabulary.h:481
bool isLeaf() const
Returns whether the node is a leaf node.
Definition: Vocabulary.h:472
static void meanValue(const std::vector< TinyMat > &descriptors, TinyMat &mean)
Calculates the mean value of a set of descriptors.
Definition: Vocabulary.h:2008
bool load(const std::string &filename)
Loads the vocabulary from a file created with save.
Definition: Vocabulary.h:1934
bool save(const std::string &filename, bool binary_compressed=false) const
Saves the vocabulary into a file.
Definition: Vocabulary.h:1843
static std::shared_ptr< Vocabulary > create(const std::vector< TinyMat > &training_features, int k=10, int L=5, WeightingType weighting=TF_IDF, ScoringType scoring=L1_NORM)
Creates a vocabulary from the training features, setting the branching factor nad the depth levels of...
Definition: Vocabulary.h:1050
void setScoringType(ScoringType type)
Changes the scoring method.
Definition: Vocabulary.h:1036
void clear()
Clears the vocabulary object.
Definition: Vocabulary.h:2001
void HKmeansStep(NodeId parent_id, const std::vector< TinyMat > &descriptors, int current_level)
Creates a level in the tree, under the parent, by running kmeans with a descriptor set...
Definition: Vocabulary.h:1138
virtual WordValue getWordWeight(WordId wid) const
Returns the weight of a word.
Definition: Vocabulary.h:1413
std::shared_ptr< GeneralScoring > m_scoring_object
Object for computing scores.
Definition: Vocabulary.h:597
Vocabulary(int k=10, int L=5, WeightingType weighting=TF_IDF, ScoringType scoring=L1_NORM)
Initiates an empty vocabulary.
Definition: Vocabulary.h:982
ScoringType m_scoring
Scoring method.
Definition: Vocabulary.h:594
static void addWeight(BowVector &bow, WordId id, WordValue v)
Returns the word id associated to a feature.
Definition: Vocabulary.h:361
float getEffectiveLevels() const
Returns the real depth levels of the tree on average.
Definition: Vocabulary.h:1388
ScoringType
Scoring type.
Definition: Vocabulary.h:97
int getDescritorSize() const
Returns the size of the descriptor employed.
Definition: Vocabulary.h:302
Tree node.
Definition: Vocabulary.h:458
virtual unsigned int size() const
Returns the number of nodes in the vocabulary.
Definition: Vocabulary.h:151
void getWordsFromNode(NodeId nid, std::vector< WordId > &words) const
Returns the ids of all the words that are under the given node id, by traversing any of the branches ...
Definition: Vocabulary.h:1789
LNorm
L-norms for normalization.
Definition: Vocabulary.h:81
WeightingType m_weighting
Weighting method.
Definition: Vocabulary.h:591
virtual TinyMat getWord(WordId wid) const
Returns the descriptor of a word.
Definition: Vocabulary.h:1405
int getBranchingFactor() const
Returns the branching factor of the tree (k)
Definition: Vocabulary.h:232
Definition: Camera.h:45
int getDescritorType() const
Returns the type of the descriptor employed normally(8U_C1, 32F_C1)
Definition: Vocabulary.h:307
float distance(const TinyMat &a, const TinyMat &b) const
Calculates the distance between two descriptors.
Definition: Vocabulary.h:2073
WeightingType
Weighting type.
Definition: Vocabulary.h:88
int m_k
Branching factor.
Definition: Vocabulary.h:585
void createScoringObject()
Creates an instance of the scoring object accoring to m_scoring.
Definition: Vocabulary.h:1000
virtual int stopWords(double minWeight)
Stops those words whose weight is below minWeight.
Definition: Vocabulary.h:1826
Node()
Empty constructor.
Definition: Vocabulary.h:466
int m_L
Depth levels.
Definition: Vocabulary.h:588
virtual NodeId getParentNode(WordId wid, int levelsup) const
Returns the id of the node that is "levelsup" levels from the word given.
Definition: Vocabulary.h:1774
Definition: Vocabulary.h:74
std::vector< Node > m_nodes
Tree nodes.
Definition: Vocabulary.h:600
Base class of scoring functions.
Definition: Vocabulary.h:612
int getDepthLevels() const
Returns the depth levels of the tree (L)
Definition: Vocabulary.h:238
double score(const BowVector &a, const BowVector &b) const
Returns the score of two vectors.
ScoringType getScoringType() const
Returns the scoring method.
Definition: Vocabulary.h:270
void setWeightingType(WeightingType type)
Changes the weighting method.
Definition: Vocabulary.h:1045
virtual ~GeneralScoring()
Required for virtual base classes.
Definition: Vocabulary.h:637
WeightingType getWeightingType() const
Returns the weighting method.
Definition: Vocabulary.h:264
virtual bool empty() const
Returns whether the vocabulary is empty (i.e.
Definition: Vocabulary.h:162