In this tutorial it will be shown how to:
- Use the BinaryDescriptor interface to extract the 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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42#include <iostream>
43#include <opencv2/opencv_modules.hpp>
44
45#ifdef HAVE_OPENCV_FEATURES2D
46
52
56
57static const char* keys =
58{ "{@image_path | | Image path }" };
59
60static void help()
61{
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;
64}
65
66int main(
int argc,
char** argv )
67{
68
71
72 if( image_path.empty() )
73 {
74 help();
75 return -1;
76 }
77
78
80 if( imageMat.
data == NULL )
81 {
82 std::cout << "Error, image could not be loaded. Please, check its path" << std::endl;
83 return -1;
84 }
85
86
88
89
91
92
93 vector<KeyLine> lines;
94
95
97 bd->detect( imageMat, lines, 2, 1, mask );
98
99
102 for ( size_t i = 0; i < lines.size(); i++ )
103 {
106 {
107
108 int R = ( rand() % (int) ( 255 + 1 ) );
109 int G = ( rand() % (int) ( 255 + 1 ) );
110 int B = ( rand() % (int) ( 255 + 1 ) );
111
112
115
116
117 line( output, pt1, pt2,
Scalar( B, G, R ), 3 );
118 }
119
120 }
121
122
123 imshow(
"LSD lines", output );
125}
126
127#else
128
130{
131 std::cerr << "OpenCV was built without features2d module" << std::endl;
132 return 0;
133}
134
135#endif
Designed for command line parsing.
Definition utility.hpp:820
n-dimensional dense array class
Definition mat.hpp:812
CV_NODISCARD_STD Mat clone() const
Creates a full copy of the array and the underlying data.
MatSize size
Definition mat.hpp:2160
uchar * data
pointer to the data
Definition mat.hpp:2140
static CV_NODISCARD_STD MatExpr ones(int rows, int cols, int type)
Returns an array of all 1's of the specified size and type.
int channels() const
Returns the number of matrix channels.
static Ptr< LSDDetector > createLSDDetector()
Creates ad LSDDetector object, using smart pointers.
Point2i Point
Definition types.hpp:209
std::string String
Definition cvstd.hpp:151
std::shared_ptr< _Tp > Ptr
Definition cvstd_wrapper.hpp:23
Scalar_< double > Scalar
Definition types.hpp:702
Point_< float > Point2f
Definition types.hpp:207
#define CV_8UC1
Definition interface.h:88
GMat mask(const GMat &src, const GMat &mask)
Applies a mask to a matrix.
void imshow(const String &winname, InputArray mat)
Displays an image in the specified window.
int waitKey(int delay=0)
Waits for a pressed key.
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR)
Loads an image from a file.
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0)
Converts an image from one color space to another.
@ COLOR_GRAY2BGR
Definition imgproc.hpp:557
void line(InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
Draws a line segment connecting two points.
int main(int argc, char *argv[])
Definition highgui_qt.cpp:3
Definition descriptor.hpp:77
"black box" representation of the file storage associated with a file on disk.
Definition core.hpp:102
A class to represent a line.
Definition descriptor.hpp:105
float endPointY
Definition descriptor.hpp:131
float startPointX
Definition descriptor.hpp:128
float endPointX
Definition descriptor.hpp:130
float startPointY
Definition descriptor.hpp:129
int octave
Definition descriptor.hpp:114
This is the result obtained from the 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
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42#include <iostream>
43#include <opencv2/opencv_modules.hpp>
44
45#ifdef HAVE_OPENCV_FEATURES2D
46
52
55
56
57static const char* keys =
58{ "{@image_path | | Image path }" };
59
60static void help()
61{
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>"
64 << std::endl;
65}
66
67int main(
int argc,
char** argv )
68{
69
72
73 if( image_path.empty() )
74 {
75 help();
76 return -1;
77 }
78
79
81 if( imageMat.
data == NULL )
82 {
83 std::cout << "Error, image could not be loaded. Please, check its path" << std::endl;
84 }
85
86
88
89
91
92
93 std::vector<KeyLine> keylines;
94 bd->detect( imageMat, keylines, mask );
95
96
98
99 bd->compute( imageMat, keylines, descriptors);
100
101}
102
103#else
104
106{
107 std::cerr << "OpenCV was built without features2d module" << std::endl;
108 return 0;
109}
110
111#endif
static Ptr< BinaryDescriptor > createBinaryDescriptor()
Create a BinaryDescriptor object with default parameters (or with the ones provided) and return a sma...
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42#include <iostream>
43#include <opencv2/opencv_modules.hpp>
44
45#ifdef HAVE_OPENCV_FEATURES2D
46
52
53#define MATCHES_DIST_THRESHOLD 25
54
57
58static const char* keys =
59{ "{@image_path1 | | Image path 1 }"
60 "{@image_path2 | | Image path 2 }" };
61
62static void help()
63{
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;
67
68}
69
70int main(
int argc,
char** argv )
71{
72
76
77 if( image_path1.empty() || image_path2.empty() )
78 {
79 help();
80 return -1;
81 }
82
83
86
87 if( imageMat1.
data == NULL || imageMat2.
data == NULL )
88 {
89 std::cout << "Error, images could not be loaded. Please, check their path" << std::endl;
90 }
91
92
95
96
98
99
100 std::vector<KeyLine> keylines1, keylines2;
102
103 ( *bd )( imageMat1, mask1, keylines1, descr1, false, false );
104 ( *bd )( imageMat2, mask2, keylines2, descr2, false, false );
105
106
107 std::vector<KeyLine> lbd_octave1, lbd_octave2;
108 Mat left_lbd, right_lbd;
109 for ( int i = 0; i < (int) keylines1.size(); i++ )
110 {
111 if( keylines1[i].octave == 0 )
112 {
113 lbd_octave1.push_back( keylines1[i] );
115 }
116 }
117
118 for ( int j = 0; j < (int) keylines2.size(); j++ )
119 {
120 if( keylines2[j].octave == 0 )
121 {
122 lbd_octave2.push_back( keylines2[j] );
124 }
125 }
126
127
129
130
131 std::vector<DMatch> matches;
132 bdm->match( left_lbd, right_lbd, matches );
133
134
135 std::vector<DMatch> good_matches;
136 for ( int i = 0; i < (int) matches.size(); i++ )
137 {
138 if( matches[i].distance < MATCHES_DIST_THRESHOLD )
139 good_matches.push_back( matches[i] );
140 }
141
142
145 std::vector<char>
mask( matches.size(), 1 );
148
149 imshow(
"Matches", outImg );
151 imwrite(
"/home/ubisum/Desktop/images/env_match/matches.jpg", outImg);
152
154
155
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 );
160
161
162 bd->compute( imageMat1, klsd1, lsd_descr1 );
163 bd->compute( imageMat2, klsd2, lsd_descr2 );
164
165
166 std::vector<KeyLine> octave0_1, octave0_2;
167 Mat leftDEscr, rightDescr;
168 for ( int i = 0; i < (int) klsd1.size(); i++ )
169 {
170 if( klsd1[i].octave == 1 )
171 {
172 octave0_1.push_back( klsd1[i] );
174 }
175 }
176
177 for ( int j = 0; j < (int) klsd2.size(); j++ )
178 {
179 if( klsd2[j].octave == 1 )
180 {
181 octave0_2.push_back( klsd2[j] );
183 }
184 }
185
186
187 std::vector<DMatch> lsd_matches;
188 bdm->match( leftDEscr, rightDescr, lsd_matches );
189
190
191 good_matches.clear();
192 for ( int i = 0; i < (int) lsd_matches.size(); i++ )
193 {
194 if( lsd_matches[i].distance < MATCHES_DIST_THRESHOLD )
195 good_matches.push_back( lsd_matches[i] );
196 }
197
198
202 std::vector<char> lsd_mask( matches.size(), 1 );
205
206 imshow(
"LSD matches", lsd_outImg );
208
209
210}
211
212#else
213
215{
216 std::cerr << "OpenCV was built without features2d module" << std::endl;
217 return 0;
218}
219
220#endif
Mat row(int y) const
Creates a matrix header for the specified matrix row.
void push_back(const _Tp &elem)
Adds elements to the bottom of the matrix.
static Scalar_< double > all(double v0)
static Ptr< BinaryDescriptorMatcher > createBinaryDescriptorMatcher()
Create a BinaryDescriptorMatcher object and return a smart pointer to it.
Size2i Size
Definition types.hpp:370
CV_EXPORTS_W bool imwrite(const String &filename, InputArray img, const std::vector< int > ¶ms=std::vector< int >())
Saves an image to a specified file.
void drawLineMatches(const Mat &img1, const std::vector< KeyLine > &keylines1, const Mat &img2, const std::vector< KeyLine > &keylines2, const std::vector< DMatch > &matches1to2, Mat &outImg, const Scalar &matchColor=Scalar::all(-1), const Scalar &singleLineColor=Scalar::all(-1), const std::vector< char > &matchesMask=std::vector< char >(), int flags=DrawLinesMatchesFlags::DEFAULT)
Draws the found matches of keylines from two images.
@ DEFAULT
Definition descriptor.hpp:1361
Sometimes, we could be interested in searching for the closest k descriptors, given an input one. This requires modifying previous code slightly:
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 be modified like:
std::vector<std::vector<DMatch> > matches;
bdm->radiusMatch( queries, matches, 30 );
Here's an example of matching among descriptors extracted 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 the previous section. Population of internal dataset can be done using the add function; such function doesn't directly add new data to the database, but it just stores it them locally. The real update happens when the 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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42#include <iostream>
43#include <opencv2/opencv_modules.hpp>
44
45#ifdef HAVE_OPENCV_FEATURES2D
46
52
53#include <vector>
54
57
58static const std::string images[] =
59{ "cameraman.jpg", "church.jpg", "church2.png", "einstein.jpg", "stuff.jpg" };
60
61static const char* keys =
62{ "{@image_path | | Image path }" };
63
64static void help()
65{
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;
68}
69
70int main(
int argc,
char** argv )
71{
72
75
76
77 int num_elements = sizeof ( images ) / sizeof ( images[0] );
78 std::vector < Mat > descriptorsMat;
79 std::vector < std::vector<KeyLine> > linesMat;
80
81
83
84
85 for ( int i = 0; i < num_elements; i++ )
86 {
87
88 std::stringstream image_path;
89 image_path << pathToImages << images[i];
90 std::cout << image_path.str().c_str() << std::endl;
91
92
93 Mat loadedImage =
imread( image_path.str().c_str(), 1 );
94 if( loadedImage.
data == NULL )
95 {
96 std::cout << "Could not load images." << std::endl;
97 help();
98 exit( -1 );
99 }
100
101
102 std::vector < KeyLine > lines;
104 bd->detect( loadedImage, lines );
105 bd->compute( loadedImage, lines, computedDescr );
106
107 descriptorsMat.push_back( computedDescr );
108 linesMat.push_back( lines );
109
110 }
111
112
114 for ( size_t j = 0; j < descriptorsMat.size(); j++ )
115 {
116 if( descriptorsMat[j].rows >= 5 )
117 queries.
push_back( descriptorsMat[j].rowRange( 0, 5 ) );
118
119 else if( descriptorsMat[j].rows > 0 && descriptorsMat[j].rows < 5 )
121 }
122
123 std::cout <<
"It has been generated a matrix of " << queries.
rows <<
" descriptors" << std::endl;
124
125
127
128
129 bdm->add( descriptorsMat );
130
131
132 std::vector < std::vector<DMatch> > matches;
133 bdm->radiusMatch( queries, matches, 30 );
134 std::cout << "size matches sample " << matches.size() << std::endl;
135
136 for ( int i = 0; i < (int) matches.size(); i++ )
137 {
138 for ( int j = 0; j < (int) matches[i].size(); j++ )
139 {
140 std::cout << "match: " << matches[i][j].queryIdx << " " << matches[i][j].trainIdx << " " << matches[i][j].distance << std::endl;
141 }
142
143 }
144
145}
146
147#else
148
150{
151 std::cerr << "OpenCV was built without features2d module" << std::endl;
152 return 0;
153}
154
155#endif
int rows
the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
Definition mat.hpp:2138