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