In this tutorial it will be shown how to:
- use the BinaryDescriptor interface to extract lines and store them in KeyLine objects
- use the same interface to compute descriptors for every extracted line
- use the BynaryDescriptorMatcher to determine matches among descriptors obtained from different images
Lines extraction and descriptors computation
In the following snippet of code, it is shown how to detect lines from an image. The LSD extractor is initialized with LSD_REFINE_ADV option; remaining parameters are left to their default values. A mask of ones is used in order to accept all extracted lines, which, at the end, are displayed using random colors for octave 0.
43 #include <opencv2/opencv_modules.hpp>
45 #ifdef HAVE_OPENCV_FEATURES2D
54 using namespace cv::line_descriptor;
57 static const char* keys =
58 {
"{@image_path | | Image path }" };
62 cout <<
"\nThis example shows the functionalities of lines extraction " <<
"furnished by BinaryDescriptor class\n"
63 <<
"Please, run this sample using a command in the form\n" <<
"./example_line_descriptor_lines_extraction <path_to_input_image>" << endl;
66 int main(
int argc,
char** argv )
72 if( image_path.empty() )
80 if( imageMat.
data == NULL )
82 std::cout <<
"Error, image could not be loaded. Please, check its path" << std::endl;
93 vector<KeyLine> lines;
97 bd->detect( imageMat, lines, 2, 1, mask );
102 for (
size_t i = 0; i < lines.size(); i++ )
108 int R = (
rand() % (int) ( 255 + 1 ) );
109 int G = (
rand() % (int) ( 255 + 1 ) );
110 int B = (
rand() % (int) ( 255 + 1 ) );
117 line( output, pt1, pt2,
Scalar( B, G, R ), 3 );
123 imshow(
"LSD lines", output );
131 std::cerr <<
"OpenCV was built without features2d module" << std::endl;
135 #endif // HAVE_OPENCV_FEATURES2D
This is the result obtained for famous cameraman image:
alternate text
Another way to extract lines is using LSDDetector class; such class uses the LSD extractor to compute lines. To obtain this result, it is sufficient to use the snippet code seen above, just modifying it by the rows
Ptr<LSDDetector> lsd = LSDDetector::createLSDDetector();
std::vector<KeyLine> keylines;
lsd->detect( imageMat, keylines, mask );
Here's the result returned by LSD detector again on cameraman picture:
alternate text
Once keylines have been detected, it is possible to compute their descriptors as shown in the following:
43 #include <opencv2/opencv_modules.hpp>
45 #ifdef HAVE_OPENCV_FEATURES2D
54 using namespace cv::line_descriptor;
57 static const char* keys =
58 {
"{@image_path | | Image path }" };
62 std::cout <<
"\nThis example shows the functionalities of lines extraction " <<
"and descriptors computation furnished by BinaryDescriptor class\n"
63 <<
"Please, run this sample using a command in the form\n" <<
"./example_line_descriptor_compute_descriptors <path_to_input_image>"
67 int main(
int argc,
char** argv )
73 if( image_path.empty() )
81 if( imageMat.
data == NULL )
83 std::cout <<
"Error, image could not be loaded. Please, check its path" << std::endl;
93 std::vector<KeyLine> keylines;
94 bd->detect( imageMat, keylines, mask );
99 bd->compute( imageMat, keylines, descriptors);
107 std::cerr <<
"OpenCV was built without features2d module" << std::endl;
111 #endif // HAVE_OPENCV_FEATURES2D
Matching among descriptors
If we have extracted descriptors from two different images, it is possible to search for matches among them. One way of doing it is matching exactly a descriptor to each input query descriptor, choosing the one at closest distance:
43 #include <opencv2/opencv_modules.hpp>
45 #ifdef HAVE_OPENCV_FEATURES2D
53 #define MATCHES_DIST_THRESHOLD 25
56 using namespace cv::line_descriptor;
58 static const char* keys =
59 {
"{@image_path1 | | Image path 1 }"
60 "{@image_path2 | | Image path 2 }" };
64 std::cout <<
"\nThis example shows the functionalities of lines extraction " <<
"and descriptors computation furnished by BinaryDescriptor class\n"
65 <<
"Please, run this sample using a command in the form\n" <<
"./example_line_descriptor_compute_descriptors <path_to_input_image 1>"
66 <<
"<path_to_input_image 2>" << std::endl;
70 int main(
int argc,
char** argv )
77 if( image_path1.empty() || image_path2.empty() )
87 if( imageMat1.
data == NULL || imageMat2.
data == NULL )
89 std::cout <<
"Error, images could not be loaded. Please, check their path" << std::endl;
100 std::vector<KeyLine> keylines1, keylines2;
103 ( *bd )( imageMat1, mask1, keylines1, descr1,
false, false );
104 ( *bd )( imageMat2, mask2, keylines2, descr2,
false, false );
107 std::vector<KeyLine> lbd_octave1, lbd_octave2;
108 Mat left_lbd, right_lbd;
109 for (
int i = 0; i < (int) keylines1.size(); i++ )
111 if( keylines1[i].octave == 0 )
118 for (
int j = 0; j < (int) keylines2.size(); j++ )
120 if( keylines2[j].octave == 0 )
122 lbd_octave2.push_back( keylines2[j] );
131 std::vector<DMatch> matches;
132 bdm->match( left_lbd, right_lbd, matches );
135 std::vector<DMatch> good_matches;
136 for (
int i = 0; i < (int) matches.size(); i++ )
138 if( matches[i].distance < MATCHES_DIST_THRESHOLD )
139 good_matches.push_back( matches[i] );
145 std::vector<char>
mask( matches.size(), 1 );
147 DrawLinesMatchesFlags::DEFAULT );
149 imshow(
"Matches", outImg );
151 imwrite(
"/home/ubisum/Desktop/images/env_match/matches.jpg", outImg);
156 std::vector<KeyLine> klsd1, klsd2;
157 Mat lsd_descr1, lsd_descr2;
158 lsd->detect( imageMat1, klsd1, 2, 2, mask1 );
159 lsd->detect( imageMat2, klsd2, 2, 2, mask2 );
162 bd->compute( imageMat1, klsd1, lsd_descr1 );
163 bd->compute( imageMat2, klsd2, lsd_descr2 );
166 std::vector<KeyLine> octave0_1, octave0_2;
167 Mat leftDEscr, rightDescr;
168 for (
int i = 0; i < (int) klsd1.size(); i++ )
170 if( klsd1[i].octave == 1 )
172 octave0_1.push_back( klsd1[i] );
177 for (
int j = 0; j < (int) klsd2.size(); j++ )
179 if( klsd2[j].octave == 1 )
181 octave0_2.push_back( klsd2[j] );
187 std::vector<DMatch> lsd_matches;
188 bdm->match( leftDEscr, rightDescr, lsd_matches );
191 good_matches.clear();
192 for (
int i = 0; i < (int) lsd_matches.size(); i++ )
194 if( lsd_matches[i].distance < MATCHES_DIST_THRESHOLD )
195 good_matches.push_back( lsd_matches[i] );
202 std::vector<char> lsd_mask( matches.size(), 1 );
204 DrawLinesMatchesFlags::DEFAULT );
206 imshow(
"LSD matches", lsd_outImg );
216 std::cerr <<
"OpenCV was built without features2d module" << std::endl;
220 #endif // HAVE_OPENCV_FEATURES2D
Sometimes, we could be interested in searching for the closest k descriptors, given an input one. This requires to modify slightly previous code:
std::vector<std::vector<DMatch> > matches;
bdm->knnMatch( descr1, descr2, matches, 6 );
In the above example, the closest 6 descriptors are returned for every query. In some cases, we could have a search radius and look for all descriptors distant at the most r from input query. Previous code must me modified:
std::vector<std::vector<DMatch> > matches;
bdm->radiusMatch( queries, matches, 30 );
Here's an example om matching among descriptors extratced from original cameraman image and its downsampled (and blurred) version:
alternate text
Querying internal database
The BynaryDescriptorMatcher class, owns an internal database that can be populated with descriptors extracted from different images and queried using one of the modalities described in previous section. Population of internal dataset can be done using the add function; such function doesn't directly add new data to database, but it just stores it them locally. The real update happens when function train is invoked or when any querying function is executed, since each of them invokes train before querying. When queried, internal database not only returns required descriptors, but, for every returned match, it is able to tell which image matched descriptor was extracted from. An example of internal dataset usage is described in the following code; after adding locally new descriptors, a radius search is invoked. This provokes local data to be transferred to dataset, which, in turn, is then queried.
43 #include <opencv2/opencv_modules.hpp>
45 #ifdef HAVE_OPENCV_FEATURES2D
56 using namespace cv::line_descriptor;
58 static const std::string images[] =
59 {
"cameraman.jpg",
"church.jpg",
"church2.png",
"einstein.jpg",
"stuff.jpg" };
61 static const char* keys =
62 {
"{@image_path | | Image path }" };
66 std::cout <<
"\nThis example shows the functionalities of radius matching " <<
"Please, run this sample using a command in the form\n"
67 <<
"./example_line_descriptor_radius_matching <path_to_input_images>/" << std::endl;
70 int main(
int argc,
char** argv )
77 int num_elements =
sizeof ( images ) /
sizeof ( images[0] );
78 std::vector < Mat > descriptorsMat;
79 std::vector < std::vector<KeyLine> > linesMat;
85 for (
int i = 0; i < num_elements; i++ )
88 std::stringstream image_path;
89 image_path << pathToImages << images[i];
90 std::cout << image_path.str().c_str() << std::endl;
93 Mat loadedImage =
imread( image_path.str().c_str(), 1 );
94 if( loadedImage.
data == NULL )
96 std::cout <<
"Could not load images." << std::endl;
102 std::vector < KeyLine > lines;
104 bd->detect( loadedImage, lines );
105 bd->compute( loadedImage, lines, computedDescr );
107 descriptorsMat.push_back( computedDescr );
108 linesMat.push_back( lines );
114 for (
size_t j = 0; j < descriptorsMat.size(); j++ )
116 if( descriptorsMat[j].rows >= 5 )
117 queries.
push_back( descriptorsMat[j].rowRange( 0, 5 ) );
119 else if( descriptorsMat[j].rows > 0 && descriptorsMat[j].rows < 5 )
123 std::cout <<
"It has been generated a matrix of " << queries.
rows <<
" descriptors" << std::endl;
129 bdm->add( descriptorsMat );
132 std::vector < std::vector<DMatch> > matches;
133 bdm->radiusMatch( queries, matches, 30 );
134 std::cout <<
"size matches sample " << matches.size() << std::endl;
136 for (
int i = 0; i < (int) matches.size(); i++ )
138 for (
int j = 0; j < (int) matches[i].size(); j++ )
140 std::cout <<
"match: " << matches[i][j].queryIdx <<
" " << matches[i][j].trainIdx <<
" " << matches[i][j].distance << std::endl;
151 std::cerr <<
"OpenCV was built without features2d module" << std::endl;
155 #endif // HAVE_OPENCV_FEATURES2D