diff --git a/src/fbow.cpp b/src/fbow.cpp index 2f0ad43..ff20a53 100644 --- a/src/fbow.cpp +++ b/src/fbow.cpp @@ -48,7 +48,7 @@ void Vocabulary::setParams(int aligment, int k, int desc_type, int desc_size, in } -void Vocabulary::transform(const cv::Mat &features, int level,fBow &result,fBow2&result2){ +void Vocabulary::transform(const cv::Mat &features, int level,fBow &result,fBow2&result2) const { if (features.rows==0) throw std::runtime_error("Vocabulary::transform No input data"); if (features.type()!=_params._desc_type) throw std::runtime_error("Vocabulary::transform features are of different type than vocabulary"); if (features.cols * features.elemSize() !=size_t(_params._desc_size)) throw std::runtime_error("Vocabulary::transform features are of different size than the vocabulary ones"); @@ -89,7 +89,7 @@ void Vocabulary::transform(const cv::Mat &features, int level,fBow &result,fBow2 } -fBow Vocabulary::transform(const cv::Mat &features) +fBow Vocabulary::transform(const cv::Mat &features) const { if (features.rows==0) throw std::runtime_error("Vocabulary::transform No input data"); if (features.type()!=_params._desc_type) throw std::runtime_error("Vocabulary::transform features are of different type than vocabulary"); diff --git a/src/fbow.h b/src/fbow.h index 02d9c2d..c62bae4 100644 --- a/src/fbow.h +++ b/src/fbow.h @@ -89,8 +89,8 @@ class FBOW_API Vocabulary Vocabulary(Vocabulary&&) = default; //transform the features stored as rows in the returned BagOfWords - fBow transform(const cv::Mat &features); - void transform(const cv::Mat &features, int level,fBow &result,fBow2&result2); + fBow transform(const cv::Mat &features) const; + void transform(const cv::Mat &features, int level,fBow &result,fBow2&result2) const; //loads/saves from a file @@ -170,24 +170,43 @@ class FBOW_API Vocabulary //CiWi are the so called block_node_info (see structure up) //Ci : either if the node is leaf (msb is set to 1) or not. If not leaf, the remaining 31 bits is the block where its children are. Else, it is the index of the feature that it represent //Wi: float value empkoyed to know the weight of a leaf node (employed in cases of bagofwords) - struct Block{ - Block(char * bsptr,uint64_t ds,uint64_t ds_wp,uint64_t fo,uint64_t co):_blockstart(bsptr),_desc_size_bytes(ds),_desc_size_bytes_wp(ds_wp),_feature_off_start(fo),_child_off_start(co){} + template + struct Block { + + // conditionally qualifies its argument as const + // depending on whether T is a const type as well + template + using conditional_const = + std::conditional_t< + std::is_const_v, + std::add_const_t, + TT>; + + Block(T* bsptr,uint64_t ds,uint64_t ds_wp,uint64_t fo,uint64_t co):_blockstart(bsptr),_desc_size_bytes(ds),_desc_size_bytes_wp(ds_wp),_feature_off_start(fo),_child_off_start(co){} Block(uint64_t ds,uint64_t ds_wp,uint64_t fo,uint64_t co):_desc_size_bytes(ds),_desc_size_bytes_wp(ds_wp),_feature_off_start(fo),_child_off_start(co){} - inline uint16_t getN()const{return (*((uint16_t*)(_blockstart)));} - inline void setN(uint16_t n){ *((uint16_t*)(_blockstart))=n;} + inline uint16_t getN() const { return (*((conditional_const*)(_blockstart))); } + inline void setN(uint16_t n) { *((conditional_const*)(_blockstart)) = n; } - inline bool isLeaf()const{return *((uint16_t*)(_blockstart)+1);} - inline void setLeaf(bool v)const{*((uint16_t*)(_blockstart)+1)=1;} + inline bool isLeaf() const { return *((conditional_const*)(_blockstart) + 1); } + inline void setLeaf(bool v) { *((conditional_const*)(_blockstart) + 1) = 1; } - inline void setParentId(uint32_t pid){*(((uint32_t*)(_blockstart))+1)=pid;} - inline uint32_t getParentId(){ return *(((uint32_t*)(_blockstart))+1);} + inline void setParentId(uint32_t pid) { *(((conditional_const*)(_blockstart)) + 1) = pid; } + inline uint32_t getParentId() const { return *(((conditional_const*)(_blockstart)) + 1); } - inline block_node_info * getBlockNodeInfo(int i){ return (block_node_info *)(_blockstart+_child_off_start+i*sizeof(block_node_info)); } - inline void setFeature(int i,const cv::Mat &feature){memcpy( _blockstart+_feature_off_start+i*_desc_size_bytes_wp,feature.ptr(0),feature.elemSize1()*feature.cols); } - inline void getFeature(int i,cv::Mat feature){ memcpy( feature.ptr(0), _blockstart+_feature_off_start+i*_desc_size_bytes,_desc_size_bytes ); } - template inline T*getFeature(int i){return (T*) (_blockstart+_feature_off_start+i*_desc_size_bytes_wp);} - char *_blockstart; + inline conditional_const* getBlockNodeInfo(int i) const + { return (conditional_const*)(_blockstart + _child_off_start + i * sizeof(block_node_info)); } + + inline void setFeature(int i,const cv::Mat &feature) + { memcpy(_blockstart + _feature_off_start + i * _desc_size_bytes_wp, feature.ptr(0), feature.elemSize1()*feature.cols); } + + inline void getFeature(int i,cv::Mat feature) const + { memcpy(feature.ptr(0), _blockstart + _feature_off_start + i * _desc_size_bytes, _desc_size_bytes); } + + template inline conditional_const*getFeature(int i) + { return (conditional_const*)(_blockstart + _feature_off_start + i * _desc_size_bytes_wp); } + + T* _blockstart; uint64_t _desc_size_bytes=0;//size of the descriptor(without padding) uint64_t _desc_size_bytes_wp=0;//size of the descriptor(includding padding) uint64_t _feature_off_start=0; @@ -196,12 +215,26 @@ class FBOW_API Vocabulary //returns a block structure pointing at block b - inline Block getBlock(uint32_t b) { assert(_data.get() != nullptr); assert(b < _params._nblocks); return Block(_data.get() + b * _params._block_size_bytes_wp, _params._desc_size, _params._desc_size_bytes_wp, _params._feature_off_start, _params._child_off_start); } + inline Block getBlock(uint32_t b) const + { + assert(_data != nullptr); + assert(b < _params._nblocks); + return Block(const_cast(_data.get()) + b * _params._block_size_bytes_wp, _params._desc_size, _params._desc_size_bytes_wp, _params._feature_off_start, _params._child_off_start); + } + + inline Block getBlock(uint32_t b) + { + assert(_data != nullptr); + assert(b < _params._nblocks); + return Block(_data.get() + b * _params._block_size_bytes_wp, _params._desc_size, _params._desc_size_bytes_wp, _params._feature_off_start, _params._child_off_start); + } + //given a block already create with getBlock, moves it to point to block b - inline void setBlock(uint32_t b, Block &block) { block._blockstart = _data.get() + b * _params._block_size_bytes_wp; } + template + inline void setBlock(uint32_t b, Block& block) const { block._blockstart = _data.get() + b * _params._block_size_bytes_wp; } //information about the cpu so that mmx,sse or avx extensions can be employed - std::shared_ptr cpu_info; + mutable std::shared_ptr cpu_info; //////////////////////////////////////////////////////////// @@ -228,14 +261,14 @@ class FBOW_API Vocabulary memset(feature,0,_nwords*sizeof(register_type )); } inline void startwithfeature(const register_type *feat_ptr){memcpy(feature,feat_ptr,_desc_size);} - virtual distType computeDist(register_type *fptr)=0; + virtual distType computeDist(const register_type *fptr)=0; }; struct L2_generic:public Lx{ virtual ~L2_generic(){ } - inline float computeDist(float *fptr){ + inline float computeDist(const float *fptr){ float d=0; for(int f=0;f<_nwords;f++) d+= (feature[f]-fptr[f])*(feature[f]-fptr[f]); return d; @@ -243,10 +276,10 @@ class FBOW_API Vocabulary }; #ifdef __ANDROID__ //fake elements to allow compilation - struct L2_avx_generic:public Lx{inline float computeDist(uint64_t *ptr){return std::numeric_limits::max();}}; - struct L2_se3_generic:public Lx{inline float computeDist(uint64_t *ptr){return std::numeric_limits::max();}}; - struct L2_sse3_16w:public Lx{inline float computeDist(uint64_t *ptr){return std::numeric_limits::max();}}; - struct L2_avx_8w:public Lx{inline float computeDist(uint64_t *ptr){return std::numeric_limits::max();}}; + struct L2_avx_generic:public Lx{inline float computeDist(const uint64_t *ptr){return std::numeric_limits::max();}}; + struct L2_se3_generic:public Lx{inline float computeDist(const uint64_t *ptr){return std::numeric_limits::max();}}; + struct L2_sse3_16w:public Lx{inline float computeDist(const uint64_t *ptr){return std::numeric_limits::max();}}; + struct L2_avx_8w:public Lx{inline float computeDist(const uint64_t *ptr){return std::numeric_limits::max();}}; @@ -254,7 +287,7 @@ class FBOW_API Vocabulary #else struct L2_avx_generic:public Lx<__m256,float,32>{ virtual ~L2_avx_generic(){} - inline float computeDist(__m256 *ptr){ + inline float computeDist(const __m256 *ptr){ __m256 sum=_mm256_setzero_ps(), sub_mult; //substract, multiply and accumulate for(int i=0;i<_nwords;i++){ @@ -269,7 +302,7 @@ class FBOW_API Vocabulary } }; struct L2_se3_generic:public Lx<__m128,float,16>{ - inline float computeDist(__m128 *ptr){ + inline float computeDist(const __m128 *ptr){ __m128 sum=_mm_setzero_ps(), sub_mult; //substract, multiply and accumulate for(int i=0;i<_nwords;i++){ @@ -285,7 +318,7 @@ class FBOW_API Vocabulary }; struct L2_sse3_16w:public Lx<__m128,float,16> { - inline float computeDist(__m128 *ptr){ + inline float computeDist(const __m128 *ptr){ __m128 sum=_mm_setzero_ps(), sub_mult; //substract, multiply and accumulate for(int i=0;i<16;i++){ @@ -302,7 +335,7 @@ class FBOW_API Vocabulary //specific for surf in avx struct L2_avx_8w:public Lx<__m256,float,32> { - inline float computeDist(__m256 *ptr){ + inline float computeDist(const __m256 *ptr){ __m256 sum=_mm256_setzero_ps(), sub_mult; //substract, multiply and accumulate @@ -323,7 +356,7 @@ class FBOW_API Vocabulary //generic hamming distance calculator struct L1_x64:public Lx{ - inline uint64_t computeDist(uint64_t *feat_ptr){ + inline uint64_t computeDist(const uint64_t *feat_ptr){ uint64_t result = 0; for(int i = 0; i < _nwords; ++i ) result += std::bitset<64>(feat_ptr[i] ^ feature[i]).count(); return result; @@ -331,7 +364,7 @@ class FBOW_API Vocabulary }; struct L1_x32:public Lx{ - inline uint32_t computeDist(uint32_t *feat_ptr){ + inline uint32_t computeDist(const uint32_t *feat_ptr){ uint32_t result = 0; for(int i = 0; i < _nwords; ++i ) result += std::bitset<32>(feat_ptr[i] ^ feature[i]).count(); return result; @@ -341,7 +374,7 @@ class FBOW_API Vocabulary //for orb struct L1_32bytes:public Lx{ - inline uint64_t computeDist(uint64_t *feat_ptr){ + inline uint64_t computeDist(const uint64_t *feat_ptr){ return uint64_popcnt(feat_ptr[0]^feature[0])+ uint64_popcnt(feat_ptr[1]^feature[1])+ uint64_popcnt(feat_ptr[2]^feature[2])+uint64_popcnt(feat_ptr[3]^feature[3]); } @@ -352,7 +385,7 @@ class FBOW_API Vocabulary }; //for akaze struct L1_61bytes:public Lx{ - inline uint64_t computeDist(uint64_t *feat_ptr){ + inline uint64_t computeDist(const uint64_t *feat_ptr){ return uint64_popcnt(feat_ptr[0]^feature[0])+ uint64_popcnt(feat_ptr[1]^feature[1])+ uint64_popcnt(feat_ptr[2]^feature[2])+uint64_popcnt(feat_ptr[3]^feature[3])+ @@ -366,7 +399,7 @@ class FBOW_API Vocabulary template - fBow _transform(const cv::Mat &features){ + fBow _transform(const cv::Mat &features) const { Computer comp; comp.setParams(_params._desc_size,_params._desc_size_bytes_wp); using DType=typename Computer::DType;//distance type @@ -374,11 +407,12 @@ class FBOW_API Vocabulary fBow result; std::pair best_dist_idx(std::numeric_limits::max(),0);//minimum distance found - block_node_info *bn_info; + const block_node_info *bn_info; for(int cur_feature=0;cur_feature(cur_feature)); //ensure feature is in a - Block c_block=getBlock(0); + //(jbfuehrer: template type can automatically be deduced since C++17) + Block c_block=getBlock(0); //copy to another structure and add padding with zeros do{ //given the current block, finds the node with minimum distance @@ -399,7 +433,7 @@ class FBOW_API Vocabulary return result; } template - void _transform2(const cv::Mat &features,uint32_t storeLevel,fBow &r1,fBow2 &r2){ + void _transform2(const cv::Mat &features,uint32_t storeLevel,fBow &r1,fBow2 &r2) const { Computer comp; comp.setParams(_params._desc_size,_params._desc_size_bytes_wp); using DType=typename Computer::DType;//distance type @@ -408,12 +442,13 @@ class FBOW_API Vocabulary r1.clear(); r2.clear(); std::pair best_dist_idx(std::numeric_limits::max(),0);//minimum distance found - block_node_info *bn_info; + const block_node_info *bn_info; int nbits=ceil(log2(_params._m_k)); for(int cur_feature=0;cur_feature(cur_feature)); //ensure feature is in a - Block c_block=getBlock(0); + //(jbfuehrer: template type can automatically be deduced since C++17) + Block c_block=getBlock(0); uint32_t level=0;//current level of recursion uint32_t curNode=0;//id of the current node of the tree //copy to another structure and add padding with zeros diff --git a/src/vocabulary_creator.cpp b/src/vocabulary_creator.cpp index 63f7978..b3cee85 100644 --- a/src/vocabulary_creator.cpp +++ b/src/vocabulary_creator.cpp @@ -6,9 +6,21 @@ inline int omp_get_max_threads(){return 1;} inline int omp_get_thread_num(){return 0;} #endif #include +#include using namespace std; namespace fbow{ +/** + * Returns a random number in the range [min..max] + * @param min + * @param max + * @return random T number in [min..max] + */ +template +static T RandomValue(T min, T max) { + return ((T)rand() / (T)RAND_MAX) * (max - min) + min; +} + void VocabularyCreator::create(fbow::Vocabulary &Voc, const cv::Mat &features, const std::string &desc_name, Params params) { std::vector vfeatures(1); @@ -108,7 +120,7 @@ void VocabularyCreator::createLevel( int parent, int curL,bool recursive){ } //initialize clusters - auto centers=getInitialClusterCenters(findices ); + auto centers= initialClusterCentersKmpp(findices ); center_features.resize(centers.size()); for(size_t i=0;i VocabularyCreator::getInitialClusterCenters(const std::ve return centers; } +std::vector VocabularyCreator::initialClusterCentersKmpp(const std::vector &findices) +{ + // Implements kmeans++ seeding algorithm + // Algorithm: + // 1. Choose one center uniformly at random from among the data points. + // 2. For each data point x, compute D(x), the distance between x and the nearest + // center that has already been chosen. + // 3. Add one new data point as a center. Each point x is chosen with probability + // proportional to D(x)^2. + // 4. Repeat Steps 2 and 3 until k centers have been chosen. + // 5. Now that the initial centers have been chosen, proceed using standard k-means + // clustering. + + std::vector centers; + centers.reserve(_params.k); + for (auto fi : findices) _features(fi).m_Dist = std::numeric_limits::max(); + + // 1. + + uint32_t ifeature = findices[rand() % findices.size()]; + + // create first cluster + centers.push_back(ifeature); + + // compute the initial distances + auto last_center_feat = _features[centers.back()]; + for (auto fi : findices) { + auto &feature = _features(fi); + feature.m_Dist = dist_func(last_center_feat, _features[fi]); + } + + while ((int)centers.size() < _params.k) + { + last_center_feat = _features[centers.back()]; + for (auto fi : findices) { + auto &feature = _features(fi); + if(feature.m_Dist > 0.0f) + feature.m_Dist = std::min(feature.m_Dist, dist_func(last_center_feat, _features[fi])); + } + + double dist_sum = std::accumulate(findices.begin(), findices.end(), 0.0, [&](float acc, const unsigned fid) { return acc + _features(fid).m_Dist; }); + if (dist_sum > 0) + { + double cut_d; + do + { + cut_d = RandomValue(0, dist_sum); + } while (cut_d == 0.0); + + double d_up_now = 0; + std::vector::const_iterator dit; + for (dit = findices.begin(); dit != findices.end(); ++dit) + { + d_up_now += _features(*dit).m_Dist; + if (d_up_now >= cut_d) break; + } + + if (dit == findices.end()) + --dit; + + centers.push_back(*dit); + + } // if dist_sum > 0 + else + break; + + } // while(used_clusters < m_k) + + return centers; +} + std::size_t VocabularyCreator::vhash(const std::vector > & v_vec) { std::size_t seed = 0; diff --git a/src/vocabulary_creator.h b/src/vocabulary_creator.h index e8a24b0..58a6921 100644 --- a/src/vocabulary_creator.h +++ b/src/vocabulary_creator.h @@ -82,6 +82,7 @@ class FBOW_API VocabularyCreator void createLevel(const std::vector &findices, int parent=0, int curL=0); void createLevel(int parent=0, int curL=0, bool recursive=true); std::vector getInitialClusterCenters(const std::vector &findices); + std::vector initialClusterCentersKmpp(const std::vector &findices); std::size_t vhash(const std::vector >& v_vec) ; @@ -148,7 +149,6 @@ class FBOW_API VocabularyCreator struct Node{ Node(){} Node(uint32_t Id,uint32_t Parent,const cv::Mat &Feature, uint32_t Feat_idx=std::numeric_limits::max() ):id(Id),parent(Parent),feature(Feature),feat_idx(Feat_idx){ - } uint32_t id=std::numeric_limits::max();//id of this node in the tree