Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/fbow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down
111 changes: 73 additions & 38 deletions src/fbow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<typename T>
struct Block {

// conditionally qualifies its argument as const
// depending on whether T is a const type as well
template<typename TT>
using conditional_const =
std::conditional_t<
std::is_const_v<T>,
std::add_const_t<TT>,
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<uint16_t>*)(_blockstart))); }
inline void setN(uint16_t n) { *((conditional_const<uint16_t>*)(_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<uint16_t>*)(_blockstart) + 1); }
inline void setLeaf(bool v) { *((conditional_const<uint16_t>*)(_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<uint32_t>*)(_blockstart)) + 1) = pid; }
inline uint32_t getParentId() const { return *(((conditional_const<uint32_t>*)(_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<char>(0),feature.elemSize1()*feature.cols); }
inline void getFeature(int i,cv::Mat feature){ memcpy( feature.ptr<char>(0), _blockstart+_feature_off_start+i*_desc_size_bytes,_desc_size_bytes ); }
template<typename T> inline T*getFeature(int i){return (T*) (_blockstart+_feature_off_start+i*_desc_size_bytes_wp);}
char *_blockstart;
inline conditional_const<block_node_info>* getBlockNodeInfo(int i) const
{ return (conditional_const<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<char>(0), feature.elemSize1()*feature.cols); }

inline void getFeature(int i,cv::Mat feature) const
{ memcpy(feature.ptr<char>(0), _blockstart + _feature_off_start + i * _desc_size_bytes, _desc_size_bytes); }

template<typename TT> inline conditional_const<TT>*getFeature(int i)
{ return (conditional_const<TT>*)(_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;
Expand All @@ -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<const char> getBlock(uint32_t b) const
{
assert(_data != nullptr);
assert(b < _params._nblocks);
return Block<const char>(const_cast<const char*>(_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<char> getBlock(uint32_t b)
{
assert(_data != nullptr);
assert(b < _params._nblocks);
return Block<char>(_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<typename T>
inline void setBlock(uint32_t b, Block<T>& 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> cpu_info;
mutable std::shared_ptr<cpu> cpu_info;


////////////////////////////////////////////////////////////
Expand All @@ -228,33 +261,33 @@ 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<float,float,4>{
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;
}
};
#ifdef __ANDROID__
//fake elements to allow compilation
struct L2_avx_generic:public Lx<uint64_t,float,32>{inline float computeDist(uint64_t *ptr){return std::numeric_limits<float>::max();}};
struct L2_se3_generic:public Lx<uint64_t,float,32>{inline float computeDist(uint64_t *ptr){return std::numeric_limits<float>::max();}};
struct L2_sse3_16w:public Lx<uint64_t,float,32>{inline float computeDist(uint64_t *ptr){return std::numeric_limits<float>::max();}};
struct L2_avx_8w:public Lx<uint64_t,float,32>{inline float computeDist(uint64_t *ptr){return std::numeric_limits<float>::max();}};
struct L2_avx_generic:public Lx<uint64_t,float,32>{inline float computeDist(const uint64_t *ptr){return std::numeric_limits<float>::max();}};
struct L2_se3_generic:public Lx<uint64_t,float,32>{inline float computeDist(const uint64_t *ptr){return std::numeric_limits<float>::max();}};
struct L2_sse3_16w:public Lx<uint64_t,float,32>{inline float computeDist(const uint64_t *ptr){return std::numeric_limits<float>::max();}};
struct L2_avx_8w:public Lx<uint64_t,float,32>{inline float computeDist(const uint64_t *ptr){return std::numeric_limits<float>::max();}};




#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++){
Expand All @@ -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++){
Expand All @@ -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++){
Expand All @@ -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

Expand All @@ -323,15 +356,15 @@ class FBOW_API Vocabulary

//generic hamming distance calculator
struct L1_x64:public Lx<uint64_t,uint64_t,8>{
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;
}
};

struct L1_x32:public Lx<uint32_t,uint32_t,8>{
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;
Expand All @@ -341,7 +374,7 @@ class FBOW_API Vocabulary

//for orb
struct L1_32bytes:public Lx<uint64_t,uint64_t,8>{
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]);
}
Expand All @@ -352,7 +385,7 @@ class FBOW_API Vocabulary
};
//for akaze
struct L1_61bytes:public Lx<uint64_t,uint64_t,8>{
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])+
Expand All @@ -366,19 +399,20 @@ class FBOW_API Vocabulary


template<typename Computer>
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
using TData=typename Computer::TData;//data type

fBow result;
std::pair<DType,uint32_t> best_dist_idx(std::numeric_limits<uint32_t>::max(),0);//minimum distance found
block_node_info *bn_info;
const block_node_info *bn_info;
for(int cur_feature=0;cur_feature<features.rows;cur_feature++){
comp.startwithfeature(features.ptr<TData>(cur_feature));
//ensure feature is in a
Block c_block=getBlock(0);
//(jbfuehrer: template type can automatically be deduced since C++17)
Block<const char> c_block=getBlock(0);
//copy to another structure and add padding with zeros
do{
//given the current block, finds the node with minimum distance
Expand All @@ -399,7 +433,7 @@ class FBOW_API Vocabulary
return result;
}
template<typename Computer>
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
Expand All @@ -408,12 +442,13 @@ class FBOW_API Vocabulary
r1.clear();
r2.clear();
std::pair<DType,uint32_t> best_dist_idx(std::numeric_limits<uint32_t>::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<features.rows;cur_feature++){
comp.startwithfeature(features.ptr<TData>(cur_feature));
//ensure feature is in a
Block c_block=getBlock(0);
//(jbfuehrer: template type can automatically be deduced since C++17)
Block<const char> 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
Expand Down
85 changes: 84 additions & 1 deletion src/vocabulary_creator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,21 @@ inline int omp_get_max_threads(){return 1;}
inline int omp_get_thread_num(){return 0;}
#endif
#include <iostream>
#include <numeric>
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 <class T>
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<cv::Mat> vfeatures(1);
Expand Down Expand Up @@ -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<centers.size();i++)
center_features[i]=_features[centers[i]];
Expand Down Expand Up @@ -188,6 +200,77 @@ std::vector<uint32_t> VocabularyCreator::getInitialClusterCenters(const std::ve
return centers;
}

std::vector<uint32_t> VocabularyCreator::initialClusterCentersKmpp(const std::vector<uint32_t> &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<uint32_t> centers;
centers.reserve(_params.k);
for (auto fi : findices) _features(fi).m_Dist = std::numeric_limits<float>::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<double>(0, dist_sum);
} while (cut_d == 0.0);

double d_up_now = 0;
std::vector<unsigned>::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<std::vector<uint32_t> > & v_vec) {
std::size_t seed = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/vocabulary_creator.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class FBOW_API VocabularyCreator
void createLevel(const std::vector<uint32_t> &findices, int parent=0, int curL=0);
void createLevel(int parent=0, int curL=0, bool recursive=true);
std::vector<uint32_t> getInitialClusterCenters(const std::vector<uint32_t> &findices);
std::vector<uint32_t> initialClusterCentersKmpp(const std::vector<uint32_t> &findices);

std::size_t vhash(const std::vector<std::vector<uint32_t> >& v_vec) ;

Expand Down Expand Up @@ -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<uint32_t>::max() ):id(Id),parent(Parent),feature(Feature),feat_idx(Feat_idx){

}

uint32_t id=std::numeric_limits<uint32_t>::max();//id of this node in the tree
Expand Down