diff --git a/modules/line_descriptor/include/opencv2/line_descriptor/descriptor.hpp b/modules/line_descriptor/include/opencv2/line_descriptor/descriptor.hpp index 32435bdf3..2bb5c0ae5 100644 --- a/modules/line_descriptor/include/opencv2/line_descriptor/descriptor.hpp +++ b/modules/line_descriptor/include/opencv2/line_descriptor/descriptor.hpp @@ -55,7 +55,6 @@ #include "sparse_hashtable.hpp" #include "types.hpp" - namespace cv { @@ -195,17 +194,17 @@ namespace cv protected: /* implementation of line detection */ - virtual void detectImpl( const Mat& image, + virtual void detectImpl( const Mat& imageSrc, std::vector& keylines, const Mat& mask=Mat() ) const; /* implementation of descriptors' computation */ - virtual void computeImpl( const Mat& image, + virtual void computeImpl( const Mat& imageSrc, std::vector& keylines, Mat& descriptors ) const; - /* function inherited by Algorithm */ + /* function inherited from Algorithm */ AlgorithmInfo* info() const; private: @@ -302,25 +301,99 @@ namespace cv const std::vector& masks=std::vector(), bool compactResult=false ); + /* store new descriptors to be inserted in dataset */ + void add( const std::vector& descriptors ); + + /* store new descriptors into dataset */ + void train(); + /* constructor with smart pointer */ static Ptr createBinaryDescriptorMatcher(); - - /* write/read data to/from file */ - virtual void read( const FileNode& ); - virtual void write( FileStorage& ) const; + /* clear dataset and internal data */ + void clear(); /* constructor */ - BinaryDescriptorMatcher(){}; + BinaryDescriptorMatcher(); /* desctructor */ - ~BinaryDescriptorMatcher(){}; + ~BinaryDescriptorMatcher(){} + + protected: + /* function inherited from Algorithm */ + AlgorithmInfo* info() const; private: - /* vector to store new desciptors */ - std::vector descriptorsVector; + /* retrieve Hamming distances */ + void checkKDistances(UINT32 * numres, + int k, + std::vector& k_distances, + int row, + int string_length) const; + + /* matrix to store new descriptors */ + Mat descriptorsMat; + + /* map storing where each bunch of descriptors benins in DS */ + std::map indexesMap; + + /* internal MiHaser representing dataset */ + Mihasher* dataset; + + /* index from which next added descriptors' bunch must begin */ + int nextAddedIndex; + + /* number of images whose descriptors are stored in DS */ + int numImages; + + /* number of descriptors in dataset */ + int descrInDS; + }; + +/* -------------------------------------------------------------------------------------------- + UTILITY FUNCTIONS + -------------------------------------------------------------------------------------------- */ + +/* struct for drawing options */ +struct CV_EXPORTS DrawLinesMatchesFlags +{ + enum + { + DEFAULT = 0, // Output image matrix will be created (Mat::create), + // i.e. existing memory of output image may be reused. + // Two source images, matches, and single keylines + // will be drawn. + DRAW_OVER_OUTIMG = 1, // Output image matrix will not be + // created (using Mat::create). Matches will be drawn + // on existing content of output image. + NOT_DRAW_SINGLE_LINES = 2 // Single keylines will not be drawn. + }; +}; + +/* draw matches between two images */ +CV_EXPORTS_W void drawLineMatches( const Mat& img1, + const std::vector& keylines1, + const Mat& img2, + const std::vector& keylines2, + const std::vector& matches1to2, + Mat& outImg, + const Scalar& matchColor=Scalar::all(-1), + const Scalar& singleLineColor=Scalar::all(-1), + const std::vector& matchesMask=std::vector(), + int flags=DrawLinesMatchesFlags::DEFAULT ); + +/* draw extracted lines on original image */ +CV_EXPORTS_W void drawKeylines( const Mat& image, + const std::vector& keylines, + Mat& outImage, + const Scalar& color=Scalar::all(-1), + int flags=DrawLinesMatchesFlags::DEFAULT ); + } + + + #endif diff --git a/modules/line_descriptor/samples/compute_descriptors.cpp b/modules/line_descriptor/samples/compute_descriptors.cpp index 559fb6731..c523a8050 100644 --- a/modules/line_descriptor/samples/compute_descriptors.cpp +++ b/modules/line_descriptor/samples/compute_descriptors.cpp @@ -58,8 +58,6 @@ int main( int argc, char** argv ) } /* create a random binary mask */ -// cv::Mat mask(imageMat.size(), CV_8UC1); -// cv::randu(mask, Scalar::all(0), Scalar::all(1)); cv::Mat mask = Mat::ones(imageMat.size(), CV_8UC1); /* create a pointer to a BinaryDescriptor object with default parameters */ diff --git a/modules/line_descriptor/samples/images/cameraman.jpg b/modules/line_descriptor/samples/images/cameraman.jpg new file mode 100644 index 000000000..9345b9215 Binary files /dev/null and b/modules/line_descriptor/samples/images/cameraman.jpg differ diff --git a/modules/line_descriptor/samples/images/church.jpg b/modules/line_descriptor/samples/images/church.jpg new file mode 100644 index 000000000..3801b4339 Binary files /dev/null and b/modules/line_descriptor/samples/images/church.jpg differ diff --git a/modules/line_descriptor/samples/images/church2.png b/modules/line_descriptor/samples/images/church2.png new file mode 100644 index 000000000..66fc80c36 Binary files /dev/null and b/modules/line_descriptor/samples/images/church2.png differ diff --git a/modules/line_descriptor/samples/images/einstein.jpg b/modules/line_descriptor/samples/images/einstein.jpg new file mode 100644 index 000000000..38d977895 Binary files /dev/null and b/modules/line_descriptor/samples/images/einstein.jpg differ diff --git a/modules/line_descriptor/samples/images/stuff.jpg b/modules/line_descriptor/samples/images/stuff.jpg new file mode 100644 index 000000000..42cb9cb6a Binary files /dev/null and b/modules/line_descriptor/samples/images/stuff.jpg differ diff --git a/modules/line_descriptor/samples/knn_matching.cpp b/modules/line_descriptor/samples/knn_matching.cpp new file mode 100644 index 000000000..728447a98 --- /dev/null +++ b/modules/line_descriptor/samples/knn_matching.cpp @@ -0,0 +1,169 @@ +#include + +#include "opencv2/core/utility.hpp" +#include "opencv2/core/private.hpp" +#include +#include +#include + +#include +#include + +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 descriptors matching\n" << + "Please, run this sample using a command in the form\n" << + "./example_line_descriptor_matching " + << "" << std::endl; + +} + +/* invert numBits bits in input char */ +uchar invertSingleBits (uchar dividend_char, int numBits) +{ + std::vector bin_vector; + long dividend; + long bin_num; + + /* convert input char to a long */ + dividend = (long)dividend_char; + + /*if a 0 has been obtained, just generate a 8-bit long vector of zeros */ + if(dividend == 0) + bin_vector = std::vector(8, 0); + + /* else, apply classic decimal to binary conversion */ + else + { + while ( dividend >= 1 ) + { + bin_num = dividend % 2; + dividend /= 2; + bin_vector.push_back(bin_num); + } + } + + /* ensure that binary vector always has length 8 */ + if(bin_vector.size()<8){ + std::vector zeros (8-bin_vector.size(), 0); + bin_vector.insert(bin_vector.end(), zeros.begin(), zeros.end()); + } + + /* invert numBits bits */ + for(int index = 0; index=0; i--) + result += bin_vector[i]*pow(2, i); + + return result; +} + +int main( int argc, char** argv ) +{ + /* get parameters from comand line */ + CommandLineParser parser( argc, argv, keys ); + String image_path1 = parser.get( 0 ); + String image_path2 = parser.get( 1 ); + + if(image_path1.empty() || image_path2.empty()) + { + help(); + return -1; + } + + + /* load image */ + cv::Mat imageMat1 = imread(image_path1, 1); + cv::Mat imageMat2 = imread(image_path2, 1); + + if(imageMat1.data == NULL || imageMat2.data == NULL) + { + std::cout << "Error, images could not be loaded. Please, check their paths" + << 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 bd = BinaryDescriptor::createBinaryDescriptor(); + + /* compute lines */ + std::vector keylines1, keylines2; + bd->detect(imageMat1, keylines1, mask1); + bd->detect(imageMat2, keylines2, mask2); + + /* compute descriptors */ + cv::Mat descr1, descr2; + bd->compute(imageMat1, keylines1, descr1); + bd->compute(imageMat2, keylines2, descr2); + + /* create a BinaryDescriptorMatcher object */ + Ptr bdm = BinaryDescriptorMatcher::createBinaryDescriptorMatcher(); + + /* make a copy of descr2 mat */ + Mat descr2Copy = descr1.clone(); + + /* randomly change some bits in original descriptors */ + srand (time(NULL)); + + for(int j = 0; j(j, randCol); + + /* change bits */ + for(int k = 1; k<=5; k++) + { + /* copy current row to train matrix */ + descr2Copy.push_back(descr1.row(j)); + + /* invert k bits */ + uchar uc = invertSingleBits(u, k); + + /* update current row in train matrix */ + descr2Copy.at(descr2Copy.rows-1, randCol) = uc; + } + } + + /* prepare a structure to host matches */ + std::vector > matches; + + /* require knn match */ + bdm->knnMatch(descr1, descr2, matches, 6); + + /* visualize matches and Hamming distances */ + for(size_t v = 0; v> 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 */ @@ -54,8 +41,10 @@ int main( int argc, char** argv ) /* load image */ - cv::Mat imageMat1 = imread(image_path1, 0); - cv::Mat imageMat2 = imread(image_path2, 0); + cv::Mat imageMat1 = imread(image_path1, 1); + cv::Mat imageMat2 = imread(image_path2, 1); + + waitKey(); if(imageMat1.data == NULL || imageMat2.data == NULL) { std::cout << "Error, images could not be loaded. Please, check their path" @@ -74,9 +63,6 @@ int main( int argc, char** argv ) 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); @@ -88,58 +74,17 @@ int main( int argc, char** argv ) /* require match */ std::vector matches; bdm->match(descr1, descr2, matches); - for(int x = 0; x mask (matches.size(), 1); + drawLineMatches(imageMat1, keylines1, imageMat2, keylines2, matches, + outImg, Scalar::all(-1), Scalar::all(-1), mask, + DrawLinesMatchesFlags::DEFAULT); + std::cout << "num dmatch " << matches.size() << std::endl; + imshow("Matches", outImg); + waitKey(); +} diff --git a/modules/line_descriptor/samples/radius_matching.cpp b/modules/line_descriptor/samples/radius_matching.cpp new file mode 100644 index 000000000..4ddf80ac8 --- /dev/null +++ b/modules/line_descriptor/samples/radius_matching.cpp @@ -0,0 +1,111 @@ +#include + +#include "opencv2/core/utility.hpp" +#include "opencv2/core/private.hpp" +#include +#include +#include + +#include +#include + +using namespace cv; + +static const std::string images[] = +{ + "cameraman.jpg", + "church.jpg", + "church2.png", + "einstein.jpg", + "stuff.jpg" +}; + +static const char* keys = +{ + "{@image_path | | Image path }" +}; + +static void help() +{ + std::cout << "\nThis example shows the functionalities of radius matching " << + "Please, run this sample using a command in the form\n" << + "./example_line_descriptor_radius_matching /" + << std::endl; +} + +int main( int argc, char** argv ) +{ + /* get parameters from comand line */ + CommandLineParser parser( argc, argv, keys ); + String pathToImages = parser.get( 0 ); + + /* create structures for hosting KeyLines and descriptors */ + int num_elements = sizeof( images ) / sizeof( images[0] ); + std::vector descriptorsMat; + std::vector > linesMat; + + /*create a pointer to a BinaryDescriptor object */ + Ptr bd = BinaryDescriptor::createBinaryDescriptor(); + + /* compute lines and descriptors */ + for(int i = 0; i lines; + Mat computedDescr; + bd->detect(loadedImage, lines); + bd->compute(loadedImage, lines, computedDescr); + + descriptorsMat.push_back(computedDescr); + linesMat.push_back(lines); + + } + + /* compose a queries matrix */ + Mat queries; + for(size_t j = 0; j= 5) + queries.push_back(descriptorsMat[j].rowRange(0, 5)); + + else if(descriptorsMat[j].rows >0 && descriptorsMat[j].rows<5) + queries.push_back(descriptorsMat[j]); + } + + std::cout << "It has been generated a matrix of " << queries.rows + << " descriptors" << std::endl; + + /* create a BinaryDescriptorMatcher object */ + Ptr bdm = BinaryDescriptorMatcher::createBinaryDescriptorMatcher(); + + /* populate matcher */ + bdm->add(descriptorsMat); + + /* compute matches */ + std::vector > matches; + bdm->radiusMatch(queries, matches, 30); + + /* print matches */ + for(size_t q = 0; q& keylines, const Mat& mask ) { - detectImpl(image, keylines, mask); + if(mask.data!=NULL && (mask.size() != image.size() || mask.type()!=CV_8UC1)) + { + + std::cout << "Mask error while detecting lines: " + << "please check its dimensions and that data type is CV_8UC1" + << std::endl; + + CV_Assert(false); + } + + else + detectImpl(image, keylines, mask); } @@ -342,15 +354,29 @@ void BinaryDescriptor::detect( const std::vector& images, /* detect lines from each image */ for(size_t counter = 0; counter& keylines, const Mat& mask ) const { + cv::Mat image; + cvtColor(imageSrc, image, COLOR_BGR2GRAY); /*check whether image depth is different from 0 */ if(image.depth() != 0) { @@ -408,13 +434,16 @@ void BinaryDescriptor::detectImpl( const Mat& image, /* delete undesired KeyLines, according to input mask */ - for(size_t keyCounter = 0; keyCounter(kl.startPointX, kl.startPointY) == 0 && - mask.at(kl.endPointX, kl.endPointY) == 0) - keylines.erase(keylines.begin() + keyCounter); + if(!mask.empty()){ + for(size_t keyCounter = 0; keyCounter(kl.startPointY, kl.startPointX) == 0 && + mask.at(kl.endPointY, kl.endPointX) == 0) + keylines.erase(keylines.begin() + keyCounter); + } } + } @@ -438,10 +467,14 @@ void BinaryDescriptor::compute( const std::vector& images, } /* implementation of descriptors computation */ -void BinaryDescriptor::computeImpl( const Mat& image, +void BinaryDescriptor::computeImpl( const Mat& imageSrc, std::vector& keylines, Mat& descriptors ) const { + /* convert input image to gray scale */ + cv::Mat image; + cvtColor(imageSrc, image, COLOR_BGR2GRAY); + /*check whether image's depth is different from 0 */ if(image.depth() != 0) { @@ -521,8 +554,8 @@ void BinaryDescriptor::computeImpl( const Mat& image, /* compute Gaussian pyramid, if image is new or pyramid was not computed before */ BinaryDescriptor *bn = const_cast(this); - if(octaveImages.size() == 0 || cv::countNonZero(image != octaveImages[0]) != 0) - bn->computeGaussianPyramid(image); + /* all structures cleared in computeGaussianPyramid */ + bn->computeGaussianPyramid(image); /* compute Sobel's derivatives */ bn->dxImg_vector.clear(); @@ -588,7 +621,7 @@ int BinaryDescriptor::OctaveKeyLines(ScaleLines &keyLines) cv::Mat currentScaledImage = octaveImages[scaleCounter]; /* create an LSD detector and store a pointer to it */ - cv::Ptr ls = cv::createLineSegmentDetector(cv::LSD_REFINE_STD); + cv::Ptr ls = cv::createLineSegmentDetector(cv::LSD_REFINE_ADV); /* prepare a vector to host extracted segments */ std::vector lines_std; @@ -602,6 +635,7 @@ int BinaryDescriptor::OctaveKeyLines(ScaleLines &keyLines) /* update lines counter */ numOfFinalLine += lines_std.size(); + } /* prepare a vector to store octave information associated to extracted lines */ diff --git a/modules/line_descriptor/src/BinaryDescriptorMatcher.cpp b/modules/line_descriptor/src/BinaryDescriptorMatcher.cpp index cb6f73b32..63757821b 100644 --- a/modules/line_descriptor/src/BinaryDescriptorMatcher.cpp +++ b/modules/line_descriptor/src/BinaryDescriptorMatcher.cpp @@ -2,14 +2,154 @@ using namespace cv; +/* constructor */ +BinaryDescriptorMatcher::BinaryDescriptorMatcher() +{ + dataset = new Mihasher(256, 32); + nextAddedIndex = 0; + numImages = 0; + descrInDS = 0; +} + /* constructor with smart pointer */ Ptr BinaryDescriptorMatcher::createBinaryDescriptorMatcher() { return Ptr(new BinaryDescriptorMatcher()); } -void BinaryDescriptorMatcher::read( const FileNode& ){} -void BinaryDescriptorMatcher::write( FileStorage& ) const{} +/* store new descriptors to be inserted in dataset */ +void BinaryDescriptorMatcher::add( const std::vector& descriptors ) +{ + for(size_t i = 0; i(nextAddedIndex, numImages)); + nextAddedIndex += descriptors[i].rows; + numImages++; + } +} + +/* store new descriptors into dataset */ +void BinaryDescriptorMatcher::train() +{ + if(!dataset) + dataset = new Mihasher(256, 32); + + if(descriptorsMat.rows >0) + dataset->populate(descriptorsMat, + descriptorsMat.rows, + descriptorsMat.cols); + + descrInDS = descriptorsMat.rows; + descriptorsMat.release(); +} + +/* clear dataset and internal data */ +void BinaryDescriptorMatcher::clear() +{ + descriptorsMat.release(); + indexesMap.clear(); + dataset = 0; + nextAddedIndex = 0; + numImages = 0; + descrInDS = 0; +} + +/* retrieve Hamming distances */ +void BinaryDescriptorMatcher::checkKDistances(UINT32 * numres, int k, std::vector & k_distances, int row, int string_length) const +{ + int k_to_found = k; + + UINT32 * numres_tmp = numres + ((string_length+1) * row); + for(int j = 0; j<(string_length+1) && k_to_found > 0; j++) + { + if((*(numres_tmp+j))>0) + { + for(int i = 0; i<(*(numres_tmp+j)) && k_to_found > 0; i++) + { + k_distances.push_back(j); + k_to_found--; + } + } + } +} + +/* for every input descriptor, + find the best matching one (from one image to a set) */ +void BinaryDescriptorMatcher::match( const Mat& queryDescriptors, + std::vector& matches, + const std::vector& masks ) +{ + /* check data validity */ + if(masks.size() !=0 && (int)masks.size() != numImages) + { + std::cout << "Error: the number of images in dataset is " << + numImages << " but match function received " << + masks.size() << " masks. Program will be terminated" + << std::endl; + + return; + } + + /* set number of requested matches to return for each query */ + dataset->setK(1); + + /* add new descriptors to dataset, if needed */ + train(); + + /* prepare structures for query */ + UINT32 *results = new UINT32[queryDescriptors.rows]; + UINT32 * numres = new UINT32[(256+1)*(queryDescriptors.rows)]; + + /* execute query */ + dataset->batchquery(results, numres, queryDescriptors, + queryDescriptors.rows, queryDescriptors.cols); + + /* compose matches */ + for(int counter = 0; counter::iterator itup; + + /* get info about original image of each returned descriptor */ + itup = indexesMap.upper_bound(results[counter] - 1); + itup--; + + /* data validity check */ + if(!masks.empty() && (masks[itup->second].rows != queryDescriptors.rows + || masks[itup->second].cols !=1)) + { + std::cout << "Error: mask " << itup->second << " in knnMatch function " + << "should have " << queryDescriptors.rows << " and " + << "1 column. Program will be terminated" + << std::endl; + + CV_Assert(false); + } + + /* create a DMatch object if required by mask of if there is + no mask at all */ + else if(masks.empty() || masks[itup->second].at(counter) !=0) + { + std::vector k_distances; + checkKDistances(numres, 1, k_distances, counter, 256); + + DMatch dm; + dm.queryIdx = counter; + dm.trainIdx = results[counter] - 1; + dm.imgIdx = itup->second; + dm.distance = k_distances[0]; + + matches.push_back(dm); + } + + } + + /* delete data */ + delete results; + delete numres; +} /* for every input descriptor, find the best matching one (for a pair of images) */ void BinaryDescriptorMatcher::match( const Mat& queryDescriptors, @@ -17,6 +157,17 @@ void BinaryDescriptorMatcher::match( const Mat& queryDescriptors, std::vector& matches, const Mat& mask ) const { + + /* check data validity */ + if(!mask.empty() && (mask.rows != queryDescriptors.rows && mask.cols != 1)) + { + std::cout << "Error: input mask should have " << + queryDescriptors.rows << " rows and 1 column. " << + "Program will be terminated" << std::endl; + + return; + } + /* create a new mihasher object */ Mihasher *mh = new Mihasher(256, 32); @@ -30,28 +181,378 @@ void BinaryDescriptorMatcher::match( const Mat& queryDescriptors, UINT32 * numres = new UINT32[(256+1)*(queryDescriptors.rows)]; /* execute query */ - mh->batchquery(results, - numres, - queryDescriptors, - queryDescriptors.rows, - queryDescriptors.cols); + mh->batchquery(results, numres, queryDescriptors, + queryDescriptors.rows, queryDescriptors.cols); /* compose matches */ - for(size_t counter = 0; counter(counter)!=0)) + if( mask.empty() || (!mask.empty() && mask.at(counter)!=0)) { + std::vector k_distances; + checkKDistances(numres, 1, k_distances, counter, 256); + DMatch dm; dm.queryIdx = counter; - dm.trainIdx = results[counter]; + dm.trainIdx = results[counter] - 1; dm.imgIdx = 0; - dm.distance = numres[counter]; + dm.distance = k_distances[0]; matches.push_back(dm); } } + /* delete data */ + delete mh; + delete results; + delete numres; + + +} + +/* for every input descriptor, + find the best k matching descriptors (for a pair of images) */ +void BinaryDescriptorMatcher::knnMatch( const Mat& queryDescriptors, + const Mat& trainDescriptors, + std::vector >& matches, + int k, + const Mat& mask, + bool compactResult ) const + +{ + /* check data validity */ + if(!mask.empty() && (mask.rows != queryDescriptors.rows || mask.cols != 1)) + { + std::cout << "Error: input mask should have " << + queryDescriptors.rows << " rows and 1 column. " << + "Program will be terminated" << std::endl; + + return; + } + + /* 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); + + /* set K */ + mh->setK(k); + + /* prepare structures for query */ + UINT32 *results = new UINT32[k*queryDescriptors.rows]; + UINT32 * numres = new UINT32[(256+1)*(queryDescriptors.rows)]; + + /* execute query */ + mh->batchquery(results, numres, queryDescriptors, + queryDescriptors.rows, queryDescriptors.cols); + + /* compose matches */ + int index = 0; + for(int counter = 0; counter tempVec; + + /* chech whether query should be ignored */ + if(!mask.empty() && mask.at(counter) == 0) + { + /* if compact result is not requested, add an empty vector */ + if(!compactResult) + matches.push_back(tempVec); + } + + /* query matches must be considered */ + else + { + std::vector k_distances; + checkKDistances(numres, k, k_distances, counter, 256); + for(int j = index; j >& matches, + int k, + const std::vector& masks, + bool compactResult ) +{ + + /* check data validity */ + if(masks.size() !=0 && (int)masks.size() != numImages) + { + std::cout << "Error: the number of images in dataset is " << + numImages << " but knnMatch function received " << + masks.size() << " masks. Program will be terminated" + << std::endl; + + return; + } + + /* set number of requested matches to return for each query */ + dataset->setK(k); + + /* add new descriptors to dataset, if needed */ + train(); + + /* prepare structures for query */ + UINT32 *results = new UINT32[k*queryDescriptors.rows]; + UINT32 * numres = new UINT32[(256+1)*(queryDescriptors.rows)]; + + /* execute query */ + dataset->batchquery(results, numres, queryDescriptors, + queryDescriptors.rows, queryDescriptors.cols); + + /* compose matches */ + int index = 0; + for(int counter = 0; counter tempVector; + + /* loop over k results returned for every query */ + for(int j = index; j::iterator itup; + itup = indexesMap.upper_bound(currentIndex); + itup--; + + /* data validity check */ + if(!masks.empty() && (masks[itup->second].rows != queryDescriptors.rows + || masks[itup->second].cols != 1)) + { + std::cout << "Error: mask " << itup->second << " in knnMatch function " + << "should have " << queryDescriptors.rows << " and " + << "1 column. Program will be terminated" + << std::endl; + + return; + } + + /* decide if, according to relative mask, returned match should be + considered */ + else if(masks.size() == 0 || masks[itup->second].at(counter) != 0) + { + std::vector k_distances; + checkKDistances(numres, k, k_distances, counter, 256); + + DMatch dm; + dm.queryIdx = counter; + dm.trainIdx = results[j] - 1; + dm.imgIdx = itup->second; + dm.distance = k_distances[j-index]; + + tempVector.push_back(dm); + } + } + + /* decide whether temporary vector should be saved */ + if((tempVector.size() == 0 && !compactResult) || tempVector.size()>0) + matches.push_back(tempVector); + + /* increment pointer */ + index += k; + } + + /* delete data */ + delete results; + delete numres; +} + +/* for every input desciptor, find all the ones falling in a + certaing matching radius (for a pair of images) */ +void BinaryDescriptorMatcher::radiusMatch( const Mat& queryDescriptors, + const Mat& trainDescriptors, + std::vector >& matches, + float maxDistance, + const Mat& mask, + bool compactResult ) const + +{ + + /* check data validity */ + if(!mask.empty() && (mask.rows != queryDescriptors.rows && mask.cols != 1)) + { + std::cout << "Error: input mask should have " << + queryDescriptors.rows << " rows and 1 column. " << + "Program will be terminated" << std::endl; + + return; + } + + /* create a new Mihasher */ + Mihasher* mh = new Mihasher(256, 32); + + /* populate Mihasher */ + Mat copy = queryDescriptors.clone(); + mh->populate(copy, copy.rows, copy.cols); + + /* set K */ + mh->setK(trainDescriptors.rows); + + /* prepare structures for query */ + UINT32 *results = new UINT32[trainDescriptors.rows*queryDescriptors.rows]; + UINT32 * numres = new UINT32[(256+1)*(queryDescriptors.rows)]; + + /* execute query */ + mh->batchquery(results, numres, queryDescriptors, + queryDescriptors.rows, queryDescriptors.cols); + + /* compose matches */ + int index = 0; + for (int i = 0; i k_distances; + checkKDistances(numres, trainDescriptors.rows, k_distances, i, 256); + + std::vector tempVector; + for(int j = 0; j(i) != 0){ + DMatch dm; + dm.queryIdx = i; + dm.trainIdx = results[j] - 1; + dm.imgIdx = 0; + dm.distance = k_distances[j-index]; + + tempVector.push_back(dm); + } + } + } + + /* decide whether temporary vector should be saved */ + if((tempVector.size() == 0 && !compactResult) || tempVector.size()>0) + matches.push_back(tempVector); + + /* increment pointer */ + index += trainDescriptors.rows; + + } + + /* delete data */ + delete mh; + delete results; + delete numres; +} + +/* for every input desciptor, find all the ones falling in a + certaing atching radius (from one image to a set) */ +void BinaryDescriptorMatcher::radiusMatch( const Mat& queryDescriptors, + std::vector >& matches, + float maxDistance, + const std::vector& masks, + bool compactResult ) +{ + + /* check data validity */ + if(masks.size() !=0 && (int)masks.size() != numImages) + { + std::cout << "Error: the number of images in dataset is " << + numImages << " but radiusMatch function received " << + masks.size() << " masks. Program will be terminated" + << std::endl; + + return; + } + + /* populate dataset */ + train(); + + /* set K */ + dataset->setK(descrInDS); + + /* prepare structures for query */ + UINT32 *results = new UINT32[descrInDS*queryDescriptors.rows]; + UINT32 * numres = new UINT32[(256+1)*(queryDescriptors.rows)]; + + /* execute query */ + dataset->batchquery(results, numres, queryDescriptors, + queryDescriptors.rows, queryDescriptors.cols); + + /* compose matches */ + int index = 0; + for(int counter = 0; counter tempVector; + for(int j = index; j k_distances; + checkKDistances(numres, descrInDS, k_distances, counter, 256); + + if(k_distances[j-index] <= maxDistance) + { + int currentIndex = results[j] - 1; + std::map::iterator itup; + itup = indexesMap.upper_bound(currentIndex); + itup--; + + /* data validity check */ + if(!masks.empty() && (masks[itup->second].rows != queryDescriptors.rows + || masks[itup->second].cols !=1)) + { + std::cout << "Error: mask " << itup->second << " in radiusMatch function " + << "should have " << queryDescriptors.rows << " and " + << "1 column. Program will be terminated" + << std::endl; + + return; + } + + /* add match if necessary */ + else if(masks.empty() || masks[itup->second].at(counter) !=0) + { + + + DMatch dm; + dm.queryIdx = counter; + dm.trainIdx = results[j] - 1; + dm.imgIdx = itup->second; + dm.distance = k_distances[j-index]; + + tempVector.push_back(dm); + } + } + } + + /* decide whether temporary vector should be saved */ + if((tempVector.size() == 0 && !compactResult) || tempVector.size()>0) + matches.push_back(tempVector); + + /* increment pointer */ + index += descrInDS; + } + + /* delete data */ + delete results; + delete numres; } diff --git a/modules/line_descriptor/src/draw.cpp b/modules/line_descriptor/src/draw.cpp new file mode 100644 index 000000000..3a0b89e77 --- /dev/null +++ b/modules/line_descriptor/src/draw.cpp @@ -0,0 +1,142 @@ +#include "precomp.hpp" + +namespace cv +{ + /* draw matches between two images */ + void drawLineMatches( const Mat& img1, const std::vector& keylines1, + const Mat& img2, const std::vector& keylines2, + const std::vector& matches1to2, + Mat& outImg, const Scalar& matchColor, + const Scalar& singleLineColor, + const std::vector& matchesMask, int flags ) + { + + /* initialize output matrix (if necessary) */ + if(flags == DrawLinesMatchesFlags::DEFAULT) + { + /* check how many rows are necessary for output matrix */ + int totalRows = img1.rows >= img2.rows?img1.rows:img2.rows; + + /* initialize output matrix */ + outImg = Mat::zeros(totalRows, img1.cols+img2.cols, img1.type()); + + } + + /* initialize random seed: */ + srand (time(NULL)); + + Scalar singleLineColorRGB; + if(singleLineColor == Scalar::all(-1)) + { + int R = (rand() % (int)(255 + 1)); + int G = (rand() % (int)(255 + 1)); + int B = (rand() % (int)(255 + 1)); + + singleLineColorRGB = Scalar(R, G, B); + } + + else + singleLineColorRGB = singleLineColor; + + /* copy input images to output images */ + Mat roi_left(outImg, Rect(0,0,img1.cols,img1.rows)); + Mat roi_right(outImg, Rect(img1.cols,0,img2.cols,img2.rows)); + img1.copyTo(roi_left); + img2.copyTo(roi_right); + + /* get columns offset */ + int offset = img1.cols; + + /* if requested, draw lines from both images */ + if(flags != DrawLinesMatchesFlags::NOT_DRAW_SINGLE_LINES) + { + for(size_t i = 0; i& keylines, + Mat& outImage, + const Scalar& color, + int flags ) + { + if(flags == DrawLinesMatchesFlags::DEFAULT) + outImage = image.clone(); + + for(size_t i = 0; i