parent
96183dd09b
commit
db189cd3db
20 changed files with 1368 additions and 50 deletions
@ -0,0 +1,60 @@ |
||||
/* dynamic array of 32-bit integers
|
||||
* arr[0] : array size |
||||
* arr[1] : array capacity |
||||
* arr[2..] : array content */ |
||||
|
||||
#ifndef __OPENCV_ARRAY32_HPP |
||||
#define __OPENCV_ARRAY32_HPP |
||||
|
||||
#include "types.hpp" |
||||
|
||||
class Array32 { |
||||
|
||||
private: |
||||
static double ARRAY_RESIZE_FACTOR; |
||||
static double ARRAY_RESIZE_ADD_FACTOR; |
||||
|
||||
public: |
||||
/* set ARRAY_RESIZE_FACTOR */ |
||||
static void setArrayResizeFactor(double arf); |
||||
|
||||
/* constructor */ |
||||
Array32(); |
||||
|
||||
/* destructor */ |
||||
~Array32(); |
||||
|
||||
/* cleaning function used in destructor */ |
||||
void cleanup(); |
||||
|
||||
/* push data */ |
||||
void push(UINT32 data); |
||||
|
||||
/* insert data at given index */ |
||||
void insert(UINT32 index, UINT32 data); |
||||
|
||||
/* return data */ |
||||
UINT32* data(); |
||||
|
||||
/* return data size */ |
||||
UINT32 size(); |
||||
|
||||
/* return capacity */ |
||||
UINT32 capacity(); |
||||
|
||||
/* definition of operator = */ |
||||
void operator= (const Array32&); |
||||
|
||||
/* print data */ |
||||
void print(); |
||||
|
||||
/* initializer */ |
||||
void init(int size); |
||||
|
||||
/* data */ |
||||
UINT32 *arr; |
||||
|
||||
|
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,66 @@ |
||||
#ifndef __OPENCV_BITARRAY_HPP |
||||
#define __OPENCV_BITARRAY_HPP |
||||
|
||||
#include "types.hpp" |
||||
#include <stdio.h> |
||||
#include <math.h> |
||||
#include <string.h> |
||||
|
||||
/* class defining a sequence of bits */ |
||||
class bitarray { |
||||
|
||||
public: |
||||
/* pointer to bits sequence and sequence's length */ |
||||
UINT32 *arr; |
||||
UINT32 length; |
||||
|
||||
/* constructor setting default values */ |
||||
bitarray() |
||||
{ |
||||
arr = NULL; |
||||
length = 0; |
||||
} |
||||
|
||||
/* constructor setting sequence's length */ |
||||
bitarray(UINT64 _bits) { |
||||
init(_bits); |
||||
} |
||||
|
||||
/* initializer of private fields */ |
||||
void init(UINT64 _bits) |
||||
{ |
||||
length = (UINT32)ceil(_bits/32.00); |
||||
arr = new UINT32[length]; |
||||
erase(); |
||||
} |
||||
|
||||
/* destructor */ |
||||
~bitarray() { |
||||
if (arr) |
||||
delete[] arr; |
||||
} |
||||
|
||||
inline void flip(UINT64 index) |
||||
{ |
||||
arr[index >> 5] ^= ((UINT32)0x01) << (index % 32); |
||||
} |
||||
|
||||
inline void set(UINT64 index) |
||||
{ |
||||
arr[index >> 5] |= ((UINT32)0x01) << (index % 32); |
||||
} |
||||
|
||||
inline UINT8 get(UINT64 index) |
||||
{ |
||||
return (arr[index >> 5] & (((UINT32)0x01) << (index % 32))) != 0; |
||||
} |
||||
|
||||
/* reserve menory for an UINT32 */ |
||||
inline void erase() |
||||
{ |
||||
memset(arr, 0, sizeof(UINT32) * length); |
||||
} |
||||
|
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,135 @@ |
||||
#ifndef __OPENCV_BITOPTS_HPP |
||||
#define __OPENCV_BITOPTS_HPP |
||||
|
||||
#define popcntll __builtin_popcountll |
||||
#define popcnt __builtin_popcount |
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
/* LUT */ |
||||
const int lookup [] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2, |
||||
2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3, |
||||
2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4, |
||||
4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4, |
||||
2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4, |
||||
4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5, |
||||
4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6, |
||||
6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, |
||||
2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3, |
||||
3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5, |
||||
4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4, |
||||
4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6, |
||||
4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5, |
||||
5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7, |
||||
6,7,7,8}; |
||||
|
||||
/*matching function */ |
||||
inline int match(UINT8*P, UINT8*Q, int codelb) |
||||
{ |
||||
switch(codelb) |
||||
{ |
||||
case 4: // 32 bit
|
||||
return popcnt(*(UINT32*)P ^ *(UINT32*)Q); |
||||
break; |
||||
case 8: // 64 bit
|
||||
return popcntll(((UINT64*)P)[0] ^ ((UINT64*)Q)[0]); |
||||
break; |
||||
case 16: // 128 bit
|
||||
return popcntll(((UINT64*)P)[0] ^ ((UINT64*)Q)[0]) \
|
||||
+ popcntll(((UINT64*)P)[1] ^ ((UINT64*)Q)[1]); |
||||
break; |
||||
case 32: // 256 bit
|
||||
return popcntll(((UINT64*)P)[0] ^ ((UINT64*)Q)[0]) \
|
||||
+ popcntll(((UINT64*)P)[1] ^ ((UINT64*)Q)[1]) \
|
||||
+ popcntll(((UINT64*)P)[2] ^ ((UINT64*)Q)[2]) \
|
||||
+ popcntll(((UINT64*)P)[3] ^ ((UINT64*)Q)[3]); |
||||
break; |
||||
case 64: // 512 bit
|
||||
return popcntll(((UINT64*)P)[0] ^ ((UINT64*)Q)[0]) \
|
||||
+ popcntll(((UINT64*)P)[1] ^ ((UINT64*)Q)[1]) \
|
||||
+ popcntll(((UINT64*)P)[2] ^ ((UINT64*)Q)[2]) \
|
||||
+ popcntll(((UINT64*)P)[3] ^ ((UINT64*)Q)[3]) \
|
||||
+ popcntll(((UINT64*)P)[4] ^ ((UINT64*)Q)[4]) \
|
||||
+ popcntll(((UINT64*)P)[5] ^ ((UINT64*)Q)[5]) \
|
||||
+ popcntll(((UINT64*)P)[6] ^ ((UINT64*)Q)[6]) \
|
||||
+ popcntll(((UINT64*)P)[7] ^ ((UINT64*)Q)[7]); |
||||
break; |
||||
default: |
||||
int output = 0; |
||||
for (int i=0; i<codelb; i++) |
||||
output+= lookup[P[i] ^ Q[i]]; |
||||
return output; |
||||
break; |
||||
} |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
/* splitting function (b <= 64) */ |
||||
inline void split (UINT64 *chunks, UINT8 *code, int m, int mplus, int b) |
||||
{ |
||||
UINT64 temp = 0x0; |
||||
int nbits = 0; |
||||
int nbyte = 0; |
||||
UINT64 mask = b==64 ? 0xFFFFFFFFFFFFFFFFLLU : ((UINT64_1 << b) - UINT64_1); |
||||
|
||||
for (int i=0; i<m; i++) |
||||
{ |
||||
while (nbits < b) |
||||
{ |
||||
temp |= ((UINT64)code[nbyte++] << nbits); |
||||
nbits += 8; |
||||
} |
||||
|
||||
chunks[i] = temp & mask; |
||||
temp = b==64 ? 0x0 : temp >> b; |
||||
nbits -= b; |
||||
|
||||
if (i == mplus-1) |
||||
{ |
||||
b--; /* b <= 63 */ |
||||
mask = ((UINT64_1 << b) - UINT64_1); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* generates the next binary code (in alphabetical order) with the
|
||||
same number of ones as the input x. Taken from |
||||
http://www.geeksforgeeks.org/archives/10375 */
|
||||
inline UINT64 next_set_of_n_elements(UINT64 x) |
||||
{ |
||||
UINT64 smallest, ripple, new_smallest; |
||||
|
||||
smallest = x & -x; |
||||
ripple = x + smallest; |
||||
new_smallest = x ^ ripple; |
||||
new_smallest = new_smallest / smallest; |
||||
new_smallest >>= 2; |
||||
return ripple | new_smallest; |
||||
} |
||||
|
||||
/* print code */ |
||||
inline void print_code(UINT64 tmp, int b) |
||||
{ |
||||
for (int j=(b-1); j>=0; j--) |
||||
{ |
||||
printf("%llu", (long long int) tmp/(1 << j)); |
||||
tmp = tmp - (tmp/(1 << j)) * (1 << j); |
||||
} |
||||
|
||||
printf("\n"); |
||||
} |
||||
|
||||
inline UINT64 choose(int n, int r) |
||||
{ |
||||
UINT64 nchooser = 1; |
||||
for (int k=0; k < r; k++) |
||||
{ |
||||
nchooser *= n-k; |
||||
nchooser /= k+1; |
||||
} |
||||
|
||||
return nchooser; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,29 @@ |
||||
#ifndef __OPENCV_BUCKET_GROUP_HPP |
||||
#define __OPENCV_BUCKET_GROUP_HPP |
||||
|
||||
#include "types.hpp" |
||||
#include "array32.hpp" |
||||
#include "bitarray.hpp" |
||||
|
||||
class BucketGroup { |
||||
|
||||
public: |
||||
/* constructor */ |
||||
BucketGroup(); |
||||
|
||||
/* destructor */ |
||||
~BucketGroup(); |
||||
|
||||
/* insert data into the bucket */ |
||||
void insert(int subindex, UINT32 data); |
||||
|
||||
/* perform a query to the bucket */ |
||||
UINT32* query(int subindex, int *size); |
||||
|
||||
/* data fields */ |
||||
UINT32 empty; |
||||
Array32 *group; |
||||
|
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,88 @@ |
||||
#ifndef __OPENCV_MIHASHER_HPP |
||||
#define __OPENCV_MIHASHER_HPP |
||||
|
||||
#include "types.hpp" |
||||
#include "bitops.hpp" |
||||
|
||||
#include "sparse_hashtable.hpp" |
||||
#include "bitarray.hpp" |
||||
|
||||
#include <opencv2/core.hpp> |
||||
#include <opencv2/imgproc.hpp> |
||||
|
||||
//#include <iostream>
|
||||
//#include <math.h>
|
||||
|
||||
|
||||
class Mihasher { |
||||
private: |
||||
|
||||
/* Bits per code */ |
||||
int B; |
||||
|
||||
/* B/8 */ |
||||
int B_over_8; |
||||
|
||||
/* Bits per chunk (must be less than 64) */ |
||||
int b; |
||||
|
||||
/* Number of chunks */ |
||||
int m; |
||||
|
||||
/* Number of chunks with b bits (have 1 bit more than others) */ |
||||
int mplus; |
||||
|
||||
/* Maximum hamming search radius (we use B/2 by default) */ |
||||
int D; |
||||
|
||||
/* Maximum hamming search radius per substring */ |
||||
int d; |
||||
|
||||
/* Maximum results to return */ |
||||
int K; |
||||
|
||||
/* Number of codes */ |
||||
UINT64 N; |
||||
|
||||
/* Table of original full-length codes */ |
||||
cv::Mat codes; |
||||
|
||||
/* Counter for eliminating duplicate results (it is not thread safe) */ |
||||
bitarray *counter; |
||||
|
||||
/* Array of m hashtables */ |
||||
SparseHashtable *H; |
||||
|
||||
/* Volume of a b-bit Hamming ball with radius s (for s = 0 to d) */ |
||||
UINT32 *xornum; |
||||
|
||||
/* Used within generation of binary codes at a certain Hamming distance */ |
||||
int power[100]; |
||||
|
||||
public: |
||||
|
||||
/* constructor */ |
||||
Mihasher(); |
||||
|
||||
/* desctructor */ |
||||
~Mihasher(); |
||||
|
||||
/* constructor 2 */ |
||||
Mihasher(int B, int m); |
||||
|
||||
/* K setter */ |
||||
void setK(int K); |
||||
|
||||
/* populate tables */ |
||||
void populate(cv::Mat & codes, UINT32 N, int dim1codes); |
||||
|
||||
/* execute a batch query */ |
||||
void batchquery (UINT32 * results, UINT32 *numres/*, qstat *stats*/,const cv::Mat & q, UINT32 numq, int dim1queries); |
||||
|
||||
private: |
||||
|
||||
/* execute a single query */ |
||||
void query(UINT32 * results, UINT32* numres/*, qstat *stats*/, UINT8 *q, UINT64 * chunks, UINT32 * res, int query_i); |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,43 @@ |
||||
#ifndef __OPENCV_SPARSE_HASHTABLE_HPP |
||||
#define __OPENCV_SPARSE_HASHTABLE_HPP |
||||
|
||||
#include "types.hpp" |
||||
#include "bucket_group.hpp" |
||||
|
||||
class SparseHashtable |
||||
{ |
||||
|
||||
private: |
||||
|
||||
/* Maximum bits per key before folding the table */ |
||||
static const int MAX_B; |
||||
|
||||
/* Bins (each bin is an Array object for duplicates of the same key) */ |
||||
BucketGroup *table; |
||||
|
||||
public: |
||||
|
||||
/* constructor */ |
||||
SparseHashtable(); |
||||
|
||||
/* destructor */ |
||||
~SparseHashtable(); |
||||
|
||||
/* initializer */ |
||||
int init(int _b); |
||||
|
||||
/* insert data */ |
||||
void insert(UINT64 index, UINT32 data); |
||||
|
||||
/* query data */ |
||||
UINT32* query(UINT64 index, int* size); |
||||
|
||||
/* Bits per index */ |
||||
int b; |
||||
|
||||
/* Number of bins */ |
||||
UINT64 size; |
||||
|
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,16 @@ |
||||
#include <inttypes.h> |
||||
|
||||
#ifndef __OPENCV_TYPES_HPP |
||||
#define __OPENCV_TYPES_HPP |
||||
|
||||
/* define data types */ |
||||
typedef uint64_t UINT64; |
||||
typedef uint32_t UINT32; |
||||
typedef uint16_t UINT16; |
||||
typedef uint8_t UINT8; |
||||
|
||||
/* define constants */ |
||||
#define UINT64_1 ((UINT64)0x01) |
||||
#define UINT32_1 ((UINT32)0x01) |
||||
|
||||
#endif |
@ -0,0 +1,145 @@ |
||||
#include <opencv2/line_descriptor.hpp> |
||||
|
||||
#include "opencv2/core/utility.hpp" |
||||
#include "opencv2/core/private.hpp" |
||||
#include <opencv2/imgproc.hpp> |
||||
#include <opencv2/features2d.hpp> |
||||
#include <opencv2/highgui.hpp> |
||||
|
||||
#include <iostream> |
||||
|
||||
using namespace cv; |
||||
|
||||
static const char* keys = |
||||
{ |
||||
"{@image_path1 | | Image path 1 }" |
||||
"{@image_path2 | | Image path 2 }" |
||||
}; |
||||
|
||||
static void help() |
||||
{ |
||||
std::cout << "\nThis example shows the functionalities of lines extraction " << |
||||
"and descriptors computation furnished by BinaryDescriptor class\n" << |
||||
"Please, run this sample using a command in the form\n" << |
||||
"./example_line_descriptor_compute_descriptors <path_to_input_image 1>" |
||||
<< "<path_to_input_image 2>" << std::endl; |
||||
|
||||
} |
||||
|
||||
inline void writeMat(cv::Mat m, std::string name, int n) |
||||
{ |
||||
std::stringstream ss; |
||||
std::string s; |
||||
ss << n; |
||||
ss >> s; |
||||
std::string fileNameConf = name + s; |
||||
cv::FileStorage fsConf(fileNameConf, cv::FileStorage::WRITE); |
||||
fsConf << "m" << m; |
||||
|
||||
fsConf.release(); |
||||
} |
||||
|
||||
int main( int argc, char** argv ) |
||||
{ |
||||
/* get parameters from comand line */ |
||||
CommandLineParser parser( argc, argv, keys ); |
||||
String image_path1 = parser.get<String>( 0 ); |
||||
String image_path2 = parser.get<String>( 1 ); |
||||
|
||||
if(image_path1.empty() || image_path2.empty()) |
||||
{ |
||||
help(); |
||||
return -1; |
||||
} |
||||
|
||||
|
||||
/* load image */ |
||||
cv::Mat imageMat1 = imread(image_path1, 0); |
||||
cv::Mat imageMat2 = imread(image_path2, 0); |
||||
if(imageMat1.data == NULL || imageMat2.data == NULL) |
||||
{ |
||||
std::cout << "Error, images could not be loaded. Please, check their path" |
||||
<< std::endl; |
||||
} |
||||
|
||||
/* create binary masks */ |
||||
cv::Mat mask1 = Mat::ones(imageMat1.size(), CV_8UC1); |
||||
cv::Mat mask2 = Mat::ones(imageMat2.size(), CV_8UC1); |
||||
|
||||
/* create a pointer to a BinaryDescriptor object with default parameters */ |
||||
Ptr<BinaryDescriptor> bd = BinaryDescriptor::createBinaryDescriptor(); |
||||
|
||||
/* compute lines */ |
||||
std::vector<KeyLine> keylines1, keylines2; |
||||
bd->detect(imageMat1, keylines1, mask1); |
||||
bd->detect(imageMat2, keylines2, mask2); |
||||
|
||||
std::cout << "lines " << keylines1.size() << " " << keylines2.size() |
||||
<< std::endl; |
||||
|
||||
/* compute descriptors */ |
||||
cv::Mat descr1, descr2; |
||||
bd->compute(imageMat1, keylines1, descr1); |
||||
bd->compute(imageMat2, keylines2, descr2); |
||||
|
||||
/* create a BinaryDescriptorMatcher object */ |
||||
Ptr<BinaryDescriptorMatcher> bdm = BinaryDescriptorMatcher::createBinaryDescriptorMatcher(); |
||||
|
||||
/* require match */ |
||||
std::vector<DMatch> matches; |
||||
bdm->match(descr1, descr2, matches); |
||||
for(int x = 0; x<matches.size(); x++) |
||||
std::cout << matches[x].queryIdx << " " << matches[x].trainIdx << std::endl; |
||||
|
||||
/* result checkout */ |
||||
cv::Mat result(descr1.size(), CV_8UC1); |
||||
std::cout << "size " << descr1.rows << " " << descr1.cols |
||||
<< " " << descr2.rows << " " << descr2.cols << std::endl; |
||||
// for(size_t i = 0; i<matches.size(); i++){
|
||||
// uchar* pointer = result.ptr(i);
|
||||
// uchar* trainPointer = descr2.ptr(matches[i].trainIdx);
|
||||
// *pointer = *trainPointer;
|
||||
// pointer++;
|
||||
// }
|
||||
|
||||
|
||||
/* write matrices */ |
||||
writeMat(descr1, "descr1", 0); |
||||
writeMat(result, "result", 0); |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,57 @@ |
||||
#include "precomp.hpp" |
||||
|
||||
using namespace cv; |
||||
|
||||
/* constructor with smart pointer */ |
||||
Ptr<BinaryDescriptorMatcher> BinaryDescriptorMatcher::createBinaryDescriptorMatcher() |
||||
{ |
||||
return Ptr<BinaryDescriptorMatcher>(new BinaryDescriptorMatcher()); |
||||
} |
||||
|
||||
void BinaryDescriptorMatcher::read( const FileNode& ){} |
||||
void BinaryDescriptorMatcher::write( FileStorage& ) const{} |
||||
|
||||
/* for every input descriptor, find the best matching one (for a pair of images) */ |
||||
void BinaryDescriptorMatcher::match( const Mat& queryDescriptors, |
||||
const Mat& trainDescriptors, |
||||
std::vector<DMatch>& matches, |
||||
const Mat& mask ) const |
||||
{ |
||||
/* create a new mihasher object */ |
||||
Mihasher *mh = new Mihasher(256, 32); |
||||
|
||||
/* populate mihasher */ |
||||
cv::Mat copy = trainDescriptors.clone(); |
||||
mh->populate(copy, copy.rows, copy.cols); |
||||
mh->setK(1); |
||||
|
||||
/* prepare structures for query */ |
||||
UINT32 *results = new UINT32[queryDescriptors.rows]; |
||||
UINT32 * numres = new UINT32[(256+1)*(queryDescriptors.rows)]; |
||||
|
||||
/* execute query */ |
||||
mh->batchquery(results, |
||||
numres, |
||||
queryDescriptors, |
||||
queryDescriptors.rows, |
||||
queryDescriptors.cols); |
||||
|
||||
/* compose matches */ |
||||
for(size_t counter = 0; counter<queryDescriptors.rows; counter++) |
||||
{ |
||||
/* create a DMatch object if required by mask of if there is
|
||||
no mask at all */ |
||||
if( mask.empty() || (!mask.empty() && mask.at<int>(counter)!=0)) |
||||
{ |
||||
DMatch dm; |
||||
dm.queryIdx = counter; |
||||
dm.trainIdx = results[counter]; |
||||
dm.imgIdx = 0; |
||||
dm.distance = numres[counter]; |
||||
|
||||
matches.push_back(dm); |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,155 @@ |
||||
/* dynamic array of 32-bit integers
|
||||
* arr[0] : array size |
||||
* arr[1] : array capacity |
||||
* arr[2..] : array content */ |
||||
|
||||
#include "precomp.hpp" |
||||
|
||||
|
||||
/* no need for the static keyword in the definition */ |
||||
double Array32::ARRAY_RESIZE_FACTOR = 1.1; // minimum is 1.0
|
||||
double Array32::ARRAY_RESIZE_ADD_FACTOR = 4; // minimum is 1
|
||||
|
||||
/* set ARRAY_RESIZE_FACTOR */ |
||||
void Array32::setArrayResizeFactor(double arf) |
||||
{ |
||||
ARRAY_RESIZE_FACTOR = arf; |
||||
} |
||||
|
||||
/* constructor */ |
||||
Array32::Array32 () |
||||
{ |
||||
arr = NULL; |
||||
} |
||||
|
||||
/* definition of operator =
|
||||
Array32& Array32::operator = (const Array32 &rhs) */ |
||||
void Array32::operator = (const Array32 &rhs) |
||||
{ |
||||
if (&rhs != this) |
||||
this->arr = rhs.arr; |
||||
} |
||||
|
||||
/* destructor */ |
||||
Array32::~Array32 () |
||||
{ |
||||
cleanup(); |
||||
} |
||||
|
||||
/* cleaning function used in destructor */ |
||||
void Array32::cleanup () |
||||
{ |
||||
free(arr); |
||||
} |
||||
|
||||
/* push data */ |
||||
void Array32::push(UINT32 Data) |
||||
{ |
||||
if (arr) |
||||
{ |
||||
if (arr[0] == arr[1]) |
||||
{ |
||||
arr[1] = std::max(ceil(arr[1]*ARRAY_RESIZE_FACTOR), |
||||
arr[1]+ARRAY_RESIZE_ADD_FACTOR); |
||||
UINT32* new_Data = static_cast<UINT32*> |
||||
(realloc (arr, sizeof(UINT32)*(2 + arr[1]))); |
||||
if (new_Data == NULL) |
||||
{ |
||||
/* could not realloc, but orig still valid */ |
||||
std::cout << "ALERT!!!! Not enough memory, operation aborted!" |
||||
<< std::endl; |
||||
exit(0); |
||||
} |
||||
else |
||||
{ |
||||
arr = new_Data; |
||||
} |
||||
|
||||
} |
||||
|
||||
arr[2 + arr[0]] = Data; |
||||
arr[0]++; |
||||
|
||||
} |
||||
|
||||
else |
||||
{ |
||||
arr = (UINT32*) malloc ((2+ARRAY_RESIZE_ADD_FACTOR)*sizeof(UINT32)); |
||||
arr[0] = 1; |
||||
arr[1] = 1; |
||||
arr[2] = Data; |
||||
} |
||||
} |
||||
|
||||
/* insert data at given index */ |
||||
void Array32::insert(UINT32 index, UINT32 Data) { |
||||
if (arr) { |
||||
if (arr[0] == arr[1]) { |
||||
arr[1] = ceil(arr[0]*1.1); |
||||
UINT32* new_data = static_cast<UINT32*> |
||||
(realloc (arr, sizeof(UINT32)*(2 + arr[1]))); |
||||
if (new_data == NULL) |
||||
{ |
||||
// could not realloc, but orig still valid
|
||||
std::cout << "ALERT!!!! Not enough memory, operation aborted!" |
||||
<< std::endl; |
||||
exit(0); |
||||
} |
||||
else |
||||
{ |
||||
arr = new_data; |
||||
} |
||||
} |
||||
|
||||
memmove(arr+(2+index)+1, arr+(2+index), (arr[0]-index)*sizeof(*arr)); |
||||
|
||||
arr[2+index] = Data; |
||||
arr[0]++; |
||||
} |
||||
|
||||
else |
||||
{ |
||||
arr = (UINT32*) malloc (3*sizeof(UINT32)); |
||||
arr[0] = 1; |
||||
arr[1] = 1; |
||||
arr[2] = Data; |
||||
} |
||||
} |
||||
|
||||
|
||||
/* return data */ |
||||
UINT32* Array32::data() |
||||
{ |
||||
return arr? arr + 2 : NULL; |
||||
} |
||||
|
||||
/* return data size */ |
||||
UINT32 Array32::size () |
||||
{ |
||||
return arr ? arr[0] : 0; |
||||
} |
||||
|
||||
/* return capacity */ |
||||
UINT32 Array32::capacity () |
||||
{ |
||||
return arr ? arr[1] : 0; |
||||
} |
||||
|
||||
/* print data */ |
||||
void Array32::print() { |
||||
for (int i=0; i<size(); i++) |
||||
printf("%d, ", arr[i+2]); |
||||
|
||||
printf("\n"); |
||||
} |
||||
|
||||
/* initializer */ |
||||
void Array32::init(int size) |
||||
{ |
||||
if (arr == NULL) |
||||
{ |
||||
arr = (UINT32*) malloc ((2+size)*sizeof(UINT32)); |
||||
arr[0] = 0; |
||||
arr[1] = size; |
||||
} |
||||
} |
@ -0,0 +1,58 @@ |
||||
#include "precomp.hpp" |
||||
|
||||
/* constructor */ |
||||
BucketGroup::BucketGroup() |
||||
{ |
||||
empty = 0; |
||||
group = NULL; |
||||
} |
||||
|
||||
/* destructor */ |
||||
BucketGroup::~BucketGroup() |
||||
{ |
||||
if (group != NULL) |
||||
delete group; |
||||
} |
||||
|
||||
/* insert data into the bucket */ |
||||
void BucketGroup::insert(int subindex, UINT32 data) |
||||
{ |
||||
if (group == NULL) |
||||
{ |
||||
group = new Array32(); |
||||
group->push(0); |
||||
} |
||||
|
||||
UINT32 lowerbits = ((UINT32)1 << subindex) - 1; |
||||
int end = popcnt(empty & lowerbits); |
||||
|
||||
if (!(empty & ((UINT32)1 << subindex))) |
||||
{ |
||||
group->insert(end, group->arr[end+2]); |
||||
empty |= (UINT32)1 << subindex; |
||||
} |
||||
|
||||
int totones = popcnt(empty); |
||||
group->insert(totones+1+group->arr[2+end+1], data); |
||||
for (int i=end+1; i<totones+1; i++) |
||||
group->arr[2+i]++; |
||||
} |
||||
|
||||
/* perform a query to the bucket */ |
||||
UINT32* BucketGroup::query(int subindex, int *size) |
||||
{ |
||||
if (empty & ((UINT32)1 << subindex)) |
||||
{ |
||||
UINT32 lowerbits = ((UINT32)1 << subindex) - 1; |
||||
int end = popcnt(empty & lowerbits); |
||||
int totones = popcnt(empty); |
||||
*size = group->arr[2+end+1]-group->arr[2+end]; |
||||
return group->arr + 2 + totones+1 + group->arr[2+end]; |
||||
} |
||||
|
||||
else |
||||
{ |
||||
*size = 0; |
||||
return NULL; |
||||
} |
||||
} |
@ -0,0 +1,227 @@ |
||||
#include "precomp.hpp" |
||||
|
||||
|
||||
/* execute a batch query */ |
||||
void Mihasher::batchquery(UINT32 * results, UINT32 *numres, const cv::Mat & queries, UINT32 numq, int dim1queries) |
||||
{ |
||||
/* create and initialize a bitarray */ |
||||
counter = new bitarray; |
||||
counter->init(N); |
||||
|
||||
UINT32 *res = new UINT32[K*(D+1)]; |
||||
UINT64 *chunks = new UINT64[m]; |
||||
UINT32 * presults = results; |
||||
UINT32 *pnumres = numres; |
||||
|
||||
/* make a copy of input queries */ |
||||
cv::Mat queries_clone = queries.clone(); |
||||
|
||||
/* set a pointer to first query (row) */ |
||||
UINT8 *pq = queries_clone.ptr(); |
||||
|
||||
/* loop over number of descriptors */ |
||||
for (size_t i=0; i<numq; i++) { |
||||
/* for every descriptor, query database */ |
||||
query(presults, pnumres, pq, chunks, res, i); |
||||
|
||||
/* move pointer to write next K indeces */ |
||||
presults += K; |
||||
pnumres += B+1; |
||||
|
||||
/* move forward pointer to current row in descriptors matrix */ |
||||
pq += dim1queries; |
||||
|
||||
} |
||||
|
||||
delete [] res; |
||||
delete [] chunks; |
||||
|
||||
delete counter; |
||||
} |
||||
|
||||
/* execute a single query */ |
||||
void Mihasher::query(UINT32* results, UINT32* numres, |
||||
UINT8 * Query, UINT64 *chunks, UINT32 *res, int query_i) |
||||
{
|
||||
/* if K == 0 that means we want everything to be processed.
|
||||
So maxres = N in that case. Otherwise K limits the results processed */ |
||||
UINT32 maxres = K ? K : N; |
||||
|
||||
/* number of results so far obtained (up to a distance of s per chunk) */ |
||||
UINT32 n = 0; |
||||
|
||||
/* number of candidates tested with full codes (not counting duplicates) */ |
||||
UINT32 nc = 0; |
||||
|
||||
/* counting everything retrieved (duplicates are counted multiple times)
|
||||
number of lookups (and xors) */ |
||||
UINT32 nl = 0; |
||||
|
||||
UINT32 nd = 0; |
||||
UINT32 *arr; |
||||
int size = 0; |
||||
UINT32 index; |
||||
int hammd; |
||||
|
||||
|
||||
counter->erase(); |
||||
memset(numres, 0, (B+1)*sizeof(*numres)); |
||||
|
||||
split(chunks, Query, m, mplus, b); |
||||
|
||||
/* the growing search radius per substring */ |
||||
int s; |
||||
|
||||
/* current b: for the first mplus substrings it is b, for the rest it is (b-1) */ |
||||
int curb = b; |
||||
|
||||
for (s = 0; s <= d && n < maxres; s++) |
||||
{ |
||||
for (int k=0; k<m; k++) { |
||||
if (k < mplus) |
||||
curb = b; |
||||
else |
||||
curb = b-1; |
||||
UINT64 chunksk = chunks[k]; |
||||
/* number of bit-strings with s number of 1s */ |
||||
nl += xornum[s+1] - xornum[s]; |
||||
|
||||
/* the bit-string with s number of 1s */ |
||||
UINT64 bitstr = 0; |
||||
for (int i=0; i<s; i++) |
||||
/* power[i] stores the location of the i'th 1 */ |
||||
power[i] = i; |
||||
/* used for stopping criterion (location of (s+1)th 1) */ |
||||
power[s] = curb+1; |
||||
|
||||
/* bit determines the 1 that should be moving to the left */ |
||||
int bit = s-1; |
||||
|
||||
/* start from the left-most 1, and move it to the left until
|
||||
it touches another one */ |
||||
|
||||
/* the loop for changing bitstr */ |
||||
while (true) |
||||
{ |
||||
if (bit != -1) |
||||
{ |
||||
bitstr ^= (power[bit] == bit) ? (UINT64)1 << power[bit] : (UINT64)3 << (power[bit]-1); |
||||
power[bit]++; |
||||
bit--; |
||||
} |
||||
|
||||
else |
||||
{ /* bit == -1 */ |
||||
/* the binary code bitstr is available for processing */ |
||||
arr = H[k].query(chunksk ^ bitstr, &size); // lookup
|
||||
if (size) |
||||
{ /* the corresponding bucket is not empty */ |
||||
nd += size; |
||||
for (int c = 0; c < size; c++) |
||||
{ |
||||
index = arr[c]; |
||||
if (!counter->get(index)) |
||||
{ /* if it is not a duplicate */ |
||||
counter->set(index); |
||||
hammd = match(codes.ptr() + (UINT64)index*(B_over_8), Query, B_over_8); |
||||
|
||||
nc++; |
||||
if (hammd <= D && numres[hammd] < maxres) |
||||
res[hammd * K + numres[hammd]] = index+1; |
||||
|
||||
numres[hammd]++; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/* end of processing */ |
||||
while (++bit < s && power[bit] == power[bit+1]-1) { |
||||
bitstr ^= (UINT64)1 << (power[bit]-1); |
||||
power[bit] = bit; |
||||
} |
||||
if (bit == s) |
||||
break; |
||||
} |
||||
} |
||||
|
||||
n = n + numres[s*m+k]; |
||||
if (n >= maxres) |
||||
break; |
||||
} |
||||
} |
||||
|
||||
n = 0; |
||||
for (s = 0; s <= D && n < K; s++ ) |
||||
{ |
||||
for (int c = 0; c < numres[s] && n < K; c++) |
||||
results[n++] = res[s*K + c]; |
||||
} |
||||
|
||||
} |
||||
|
||||
/* constructor 2 */ |
||||
Mihasher::Mihasher(int _B, int _m) |
||||
{ |
||||
B = _B; |
||||
B_over_8 = B/8; |
||||
m = _m; |
||||
b = ceil((double)B/m); |
||||
|
||||
/* assuming that B/2 is large enough radius to include
|
||||
all of the k nearest neighbors */ |
||||
D = ceil(B/2.0); |
||||
d = ceil((double)D/m); |
||||
|
||||
/* mplus is the number of chunks with b bits
|
||||
(m-mplus) is the number of chunks with (b-1) bits */ |
||||
mplus = B - m * (b-1); |
||||
|
||||
xornum = new UINT32 [d+2]; |
||||
xornum[0] = 0; |
||||
for (int i=0; i<=d; i++) |
||||
xornum[i+1] = xornum[i] + choose(b, i); |
||||
|
||||
H = new SparseHashtable[m]; |
||||
|
||||
/* H[i].init might fail */ |
||||
for (int i=0; i<mplus; i++) |
||||
H[i].init(b); |
||||
for (int i=mplus; i<m; i++) |
||||
H[i].init(b-1); |
||||
} |
||||
|
||||
/* K setter */ |
||||
void Mihasher::setK(int _K) |
||||
{ |
||||
K = _K; |
||||
} |
||||
|
||||
/* desctructor */ |
||||
Mihasher::~Mihasher() |
||||
{ |
||||
delete[] xornum; |
||||
delete[] H; |
||||
} |
||||
|
||||
/* populate tables */ |
||||
void Mihasher::populate(cv::Mat & _codes, UINT32 _N, int dim1codes) |
||||
{ |
||||
N = _N; |
||||
codes = _codes; |
||||
UINT64 * chunks = new UINT64[m]; |
||||
|
||||
UINT8 * pcodes = codes.ptr(); |
||||
for (UINT64 i=0; i<N; i++, pcodes += dim1codes) |
||||
{ |
||||
split(chunks, pcodes, m, mplus, b); |
||||
|
||||
for (int k=0; k<m; k++) |
||||
H[k].insert(chunks[k], i); |
||||
|
||||
if (i % (int)ceil(N/1000.0) == 0) |
||||
fflush(stdout); |
||||
} |
||||
|
||||
delete [] chunks; |
||||
} |
||||
|
@ -0,0 +1,40 @@ |
||||
#include "precomp.hpp" |
||||
|
||||
const int SparseHashtable::MAX_B = 37; |
||||
|
||||
/* constructor */ |
||||
SparseHashtable::SparseHashtable() |
||||
{ |
||||
table = NULL; |
||||
size = 0; |
||||
b = 0; |
||||
} |
||||
|
||||
/* initializer */ |
||||
int SparseHashtable::init(int _b) |
||||
{ |
||||
b = _b; |
||||
|
||||
if (b < 5 || b > MAX_B || b > sizeof(UINT64)*8) |
||||
return 1; |
||||
|
||||
size = UINT64_1 << (b-5); // size = 2 ^ b
|
||||
table = (BucketGroup*) calloc(size, sizeof(BucketGroup)); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/* destructor */ |
||||
SparseHashtable::~SparseHashtable () { |
||||
free(table); |
||||
} |
||||
|
||||
/* insert data */ |
||||
void SparseHashtable::insert(UINT64 index, UINT32 data) { |
||||
table[index >> 5].insert((int)(index % 32), data); |
||||
} |
||||
|
||||
/* query data */ |
||||
UINT32* SparseHashtable::query(UINT64 index, int *Size) { |
||||
return table[index >> 5].query((int)(index % 32), Size); |
||||
} |
Loading…
Reference in new issue