OpenCV 4.11.0
开源计算机视觉
加载中…
搜索中…
无匹配项
线特征教程

本教程将演示如何

  • 使用BinaryDescriptor接口提取线段并将其存储在KeyLine对象中
  • 使用相同的接口为每个提取的线段计算描述符
  • 使用BynaryDescriptorMatcher确定从不同图像获得的描述符之间的匹配

线段提取和描述符计算

以下代码片段演示了如何从图像中检测线段。LSD 提取器使用LSD_REFINE_ADV选项初始化;其余参数保留其默认值。为了接受所有提取的线段,使用了全为一的掩码,最后使用 0 八度音阶的随机颜色显示这些线段。

1/*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // 重要提示:下载、复制、安装或使用前请阅读。
4 //
5 // 下载、复制、安装或使用本软件即表示您同意本许可协议。
6 // 如果你不同意本许可协议,请不要下载、安装、
7 // 复制或使用本软件。
8 //
9 //
10 // 许可协议
11 // 适用于开源计算机视觉库
12 //
13 // 版权所有 (C) 2014,Biagio Montesano,保留所有权利。
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 //M*/
41
42#include <iostream>
43#include <opencv2/opencv_modules.hpp>
44
45#ifdef HAVE_OPENCV_FEATURES2D
46
49#include <opencv2/imgproc.hpp>
51#include <opencv2/highgui.hpp>
52
53using namespace cv;
54using namespace cv::line_descriptor;
55using namespace std;
56
57static const char* keys =
58{ "{@image_path | | Image path }" };
59
60static void help()
61{
62 cout << "\n此示例演示了BinaryDescriptor类提供的线段提取功能\n"
63 << "请使用以下形式的命令运行此示例:\n" << "./example_line_descriptor_lines_extraction <path_to_input_image>" << endl;
64}
65
66int main( int argc, char** argv )
67{
68 /* 从命令行获取参数 */
69 CommandLineParser parser( argc, argv, keys );
70 String image_path = parser.get<String>( 0 );
71
72 if( image_path.empty() )
73 {
74 help();
75 return -1;
76 }
77
78 /* 加载图像 */
79 cv::Mat imageMat = imread( image_path, 1 );
80 if( imageMat.data == NULL )
81 {
82 std::cout << "错误,图像无法加载。请检查其路径" << std::endl;
83 return -1;
84 }
85
86 /* 创建一个随机二值掩码 */
87 cv::Mat mask = Mat::ones( imageMat.size(), CV_8UC1 );
88
89 /* 使用默认参数创建一个指向BinaryDescriptor对象的指针 */
90 Ptr<LSDDetector> bd = LSDDetector::createLSDDetector();
91
92 /* 创建一个结构体来存储提取的线段 */
93 vector<KeyLine> lines;
94
95 /* 提取线段 */
96 cv::Mat output = imageMat.clone();
97 bd->detect( imageMat, lines, 2, 1, mask );
98
99 /* 绘制从第0个octave提取的线段 */
100 if( output.channels() == 1 )
101 cvtColor( output, output, COLOR_GRAY2BGR );
102 for ( size_t i = 0; i < lines.size(); i++ )
103 {
104 KeyLine kl = lines[i];
105 if( kl.octave == 0)
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 /* 获取线段的端点 */
113 Point pt1 = Point2f( kl.startPointX, kl.startPointY );
114 Point pt2 = Point2f( kl.endPointX, kl.endPointY );
115
116 /* 绘制线段 */
117 line( output, pt1, pt2, Scalar( B, G, R ), 3 );
118 }
119
120 }
121
122 /* 在图像上显示线段 */
123 imshow( "LSD lines", output );
124 waitKey();
125}
126
127#else
128
129int main()
130{
131 std::cerr << "OpenCV 构建时未包含 features2d 模块" << std::endl;
132 return 0;
133}
134
135#endif // HAVE_OPENCV_FEATURES2D
用于命令行解析。
定义 utility.hpp:890
N维稠密数组类
定义 mat.hpp:829
CV_NODISCARD_STD Mat clone() const
创建数组及其底层数据的完整副本。
MatSize size
定义 mat.hpp:2177
uchar * data
指向数据的指针
定义 mat.hpp:2157
int channels() const
返回矩阵通道数。
std::string String
定义 cvstd.hpp:151
std::shared_ptr< _Tp > Ptr
定义 cvstd_wrapper.hpp:23
#define CV_8UC1
定义 interface.h:88
GMat mask(const GMat &src, const GMat &mask)
将掩码应用于矩阵。
void imshow(const String &winname, InputArray mat)
在指定的窗口中显示图像。
int waitKey(int delay=0)
等待按键按下。
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件中加载图像。
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
将图像从一个颜色空间转换为另一个颜色空间。
void line(InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
绘制连接两点的线段。
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3
定义 descriptor.hpp:77
定义 core.hpp:107
STL 命名空间。
表示一条线的类。
定义 descriptor.hpp:105
float endPointY
定义 descriptor.hpp:131
float startPointX
定义 descriptor.hpp:128
float endPointX
定义 descriptor.hpp:130
float startPointY
定义 descriptor.hpp:129
int octave
定义 descriptor.hpp:114

这是从著名的 cameraman 图像中获得的结果

替换文本

另一种提取线的方法是使用 *LSDDetector* 类;此类使用 LSD 提取器来计算线。要获得此结果,只需修改上面的代码片段即可。

// 创建指向 LSDDetector 对象的指针
Ptr<LSDDetector> lsd = LSDDetector::createLSDDetector();
// 计算线
std::vector<KeyLine> keylines;
lsd->detect( imageMat, keylines, mask );

这是 LSD 检测器再次在 cameraman 图片上返回的结果

替换文本

检测到关键线后,可以计算它们的描述符,如下所示

1/*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // 重要提示:下载、复制、安装或使用前请阅读。
4 //
5 // 下载、复制、安装或使用本软件即表示您同意本许可协议。
6 // 如果你不同意本许可协议,请不要下载、安装、
7 // 复制或使用本软件。
8 //
9 //
10 // 许可协议
11 // 适用于开源计算机视觉库
12 //
13 // 版权所有 (C) 2014,Biagio Montesano,保留所有权利。
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 //M*/
41
42#include <iostream>
43#include <opencv2/opencv_modules.hpp>
44
45#ifdef HAVE_OPENCV_FEATURES2D
46
49#include <opencv2/imgproc.hpp>
51#include <opencv2/highgui.hpp>
52
53using namespace cv;
54using namespace cv::line_descriptor;
55
56
57static const char* keys =
58{ "{@image_path | | Image path }" };
59
60static void help()
61{
62 std::cout << "\n此示例演示了 BinaryDescriptor 类提供的线提取和描述符计算功能\n"
63 << "请使用以下形式的命令运行此示例:\n" << "./example_line_descriptor_compute_descriptors <输入图像路径>"
64 << std::endl;
65}
66
67int main( int argc, char** argv )
68{
69 /* 从命令行获取参数 */
70 CommandLineParser parser( argc, argv, keys );
71 String image_path = parser.get<String>( 0 );
72
73 if( image_path.empty() )
74 {
75 help();
76 return -1;
77 }
78
79 /* 加载图像 */
80 cv::Mat imageMat = imread( image_path, 1 );
81 if( imageMat.data == NULL )
82 {
83 std::cout << "错误,图像无法加载。请检查其路径" << std::endl;
84 }
85
86 /* 创建二进制掩码 */
87 cv::Mat mask = Mat::ones( imageMat.size(), CV_8UC1 );
88
89 /* 创建一个指向具有默认参数的 BinaryDescriptor 对象的指针 */
90 Ptr<BinaryDescriptor> bd = BinaryDescriptor::createBinaryDescriptor();
91
92 /* 计算线 */
93 std::vector<KeyLine> keylines;
94 bd->detect( imageMat, keylines, mask );
95
96 /* 计算描述符 */
97 cv::Mat descriptors;
98
99 bd->compute( imageMat, keylines, descriptors);
100
101}
102
103#else
104
105int main()
106{
107 std::cerr << "OpenCV 是没有 features2d 模块构建的" << std::endl;
108 return 0;
109}
110
111#endif // HAVE_OPENCV_FEATURES2D

描述符之间的匹配

如果我们已经从两张不同的图像中提取了描述符,则可以在它们之间搜索匹配项。一种方法是将一个描述符精确地匹配到每个输入查询描述符,选择距离最近的一个

1/*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // 重要提示:下载、复制、安装或使用前请阅读。
4 //
5 // 下载、复制、安装或使用本软件即表示您同意本许可协议。
6 // 如果你不同意本许可协议,请不要下载、安装、
7 // 复制或使用本软件。
8 //
9 //
10 // 许可协议
11 // 适用于开源计算机视觉库
12 //
13 // 版权所有 (C) 2014,Biagio Montesano,保留所有权利。
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 //M*/
41
42#include <iostream>
43#include <opencv2/opencv_modules.hpp>
44
45#ifdef HAVE_OPENCV_FEATURES2D
46
49#include <opencv2/imgproc.hpp>
51#include <opencv2/highgui.hpp>
52
53#define MATCHES_DIST_THRESHOLD 25
54
55using namespace cv;
56using namespace cv::line_descriptor;
57
58static const char* keys =
59{ "{@image_path1 | | 图像路径 1 }"
60 "{@image_path2 | | 图像路径 2 }" };
61
62static void help()
63{
64 std::cout << "\n此示例演示了 BinaryDescriptor 类提供的线提取和描述符计算功能\n"
65 << "请使用以下形式的命令运行此示例:\n" << "./example_line_descriptor_compute_descriptors <输入图像路径 1>"
66 << "<输入图像路径 2>" << std::endl;
67
68}
69
70int main( int argc, char** argv )
71{
72 /* 从命令行获取参数 */
73 CommandLineParser parser( argc, argv, keys );
74 String image_path1 = parser.get<String>( 0 );
75 String image_path2 = parser.get<String>( 1 );
76
77 if( image_path1.empty() || image_path2.empty() )
78 {
79 help();
80 return -1;
81 }
82
83 /* 加载图像 */
84 cv::Mat imageMat1 = imread( image_path1, 1 );
85 cv::Mat imageMat2 = imread( image_path2, 1 );
86
87 if( imageMat1.data == NULL || imageMat2.data == NULL )
88 {
89 std::cout << "错误,图像无法加载。请检查路径" << std::endl;
90 }
91
92 /* 创建二值掩码 */
93 cv::Mat mask1 = Mat::ones( imageMat1.size(), CV_8UC1 );
94 cv::Mat mask2 = Mat::ones( imageMat2.size(), CV_8UC1 );
95
96 /* 创建一个使用默认参数的BinaryDescriptor对象指针 */
97 Ptr<BinaryDescriptor> bd = BinaryDescriptor::createBinaryDescriptor( );
98
99 /* 计算线段和描述子 */
100 std::vector<KeyLine> keylines1, keylines2;
101 cv::Mat descr1, descr2;
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] );
114 left_lbd.push_back( descr1.row( 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] );
123 right_lbd.push_back( descr2.row( j ) );
124 }
125 }
126
127 /* 创建一个BinaryDescriptorMatcher对象 */
128 Ptr<BinaryDescriptorMatcher> bdm = BinaryDescriptorMatcher::createBinaryDescriptorMatcher();
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 /* 绘制匹配结果 */
143 cv::Mat outImg;
144 cv::Mat scaled1, scaled2;
145 std::vector<char> mask( matches.size(), 1 );
146 drawLineMatches( imageMat1, lbd_octave1, imageMat2, lbd_octave2, good_matches, outImg, Scalar::all( -1 ), Scalar::all( -1 ), mask,
147 DrawLinesMatchesFlags::DEFAULT );
148
149 imshow( "Matches", outImg );
150 waitKey();
151 imwrite("/home/ubisum/Desktop/images/env_match/matches.jpg", outImg);
152 /* 创建一个LSD检测器 */
153 Ptr<LSDDetector> lsd = LSDDetector::createLSDDetector();
154
155 /* 检测线段 */
156 `std::vector<KeyLine> klsd1, klsd2;`
157 `cv::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 `cv::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]);`
173 `leftDEscr.push_back(lsd_descr1.row(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]);`
182 `rightDescr.push_back(lsd_descr2.row(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 /* 绘制匹配结果 */
199 `cv::Mat lsd_outImg;`
200 `resize(imageMat1, imageMat1, cv::Size(imageMat1.cols / 2, imageMat1.rows / 2), 0, 0, INTER_LINEAR_EXACT);`
201 `resize(imageMat2, imageMat2, cv::Size(imageMat2.cols / 2, imageMat2.rows / 2), 0, 0, INTER_LINEAR_EXACT);`
202 `std::vector<char> lsd_mask(matches.size(), 1);`
203 `drawLineMatches(imageMat1, octave0_1, imageMat2, octave0_2, good_matches, lsd_outImg, cv::Scalar::all(-1), cv::Scalar::all(-1), lsd_mask,`
204 `DrawLinesMatchesFlags::DEFAULT);`
205
206 `imshow("LSD matches", lsd_outImg);`
207 `waitKey();`
208
209
210}
211
212#else
213
214`int main()`
215{
216 `std::cerr << "OpenCV was built without features2d module" << std::endl;`
217 `return 0;`
218}
219
220#endif // HAVE_OPENCV_FEATURES2D
`Mat row(int y) const`
创建指定矩阵行的矩阵头。
`void push_back(const _Tp &elem)`
在矩阵底部添加元素。
用于指定图像或矩形大小的模板类。
定义 types.hpp:335
`CV_EXPORTS_W bool imwrite(const String &filename, InputArray img, const std::vector< int > &params=std::vector< int >())`
将图像保存到指定文件。
`void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)`
调整图像大小。
`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)`
绘制两幅图像中找到的关键线匹配结果。

有时,我们可能感兴趣的是搜索给定输入描述符后最近的 *k* 个描述符。这需要稍微修改之前的代码

// 准备一个结构来保存匹配结果
`std::vector<std::vector<DMatch> > matches;`
// 需要knn匹配
`bdm->knnMatch(descr1, descr2, matches, 6);`

在上面的例子中,对于每个查询,返回最近的6个描述符。在某些情况下,我们可以有一个搜索半径,并查找距离输入查询最多 *r* 的所有描述符。之前的代码必须修改为:

// 准备一个结构来保存匹配结果
`std::vector<std::vector<DMatch> > matches;`
// 计算匹配
`bdm->radiusMatch(queries, matches, 30);`

这是一个在从原始cameraman图像及其降采样(和模糊)版本中提取的描述符之间进行匹配的示例

替换文本

查询内部数据库

BynaryDescriptorMatcher 类拥有一个内部数据库,可以填充从不同图像中提取的描述符,并使用上一节中描述的一种模式进行查询。内部数据集的填充可以使用 `add` 函数完成;该函数不会直接向数据库添加新数据,而只是将它们本地存储。当调用 `train` 函数或执行任何查询函数时,实际的更新才会发生,因为它们中的每一个都会在查询之前调用 `train`。查询时,内部数据库不仅返回所需的描述符,而且对于每个返回的匹配项,它都能确定匹配描述符是从哪个图像中提取的。以下代码描述了内部数据集使用的一个示例;在本地添加新的描述符后,将调用半径搜索。这会触发将本地数据传输到数据集,然后对数据集进行查询。

1/*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // 重要提示:下载、复制、安装或使用前请阅读。
4 //
5 // 下载、复制、安装或使用本软件即表示您同意本许可协议。
6 // 如果你不同意本许可协议,请不要下载、安装、
7 // 复制或使用本软件。
8 //
9 //
10 // 许可协议
11 // 适用于开源计算机视觉库
12 //
13 // 版权所有 (C) 2014,Biagio Montesano,保留所有权利。
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 //M*/
41
42#include <iostream>
43#include <opencv2/opencv_modules.hpp>
44
45#ifdef HAVE_OPENCV_FEATURES2D
46
49#include <opencv2/imgproc.hpp>
51#include <opencv2/highgui.hpp>
52
53#include <vector>
54
55using namespace cv;
56using namespace cv::line_descriptor;
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 << "\n此示例演示了半径匹配的功能,请使用以下格式的命令运行此示例:\n"
67 << "./example_line_descriptor_radius_matching <输入图像路径>/" << std::endl;
68}
69
70int main( int argc, char** argv )
71{
72 /* 从命令行获取参数 */
73 CommandLineParser parser( argc, argv, keys );
74 String pathToImages = parser.get < String > ( 0 );
75
76 /* 创建用于存储 KeyLines 和描述符的结构 */
77 int num_elements = sizeof ( images ) / sizeof ( images[0] );
78 std::vector < Mat > descriptorsMat;
79 std::vector < std::vector<KeyLine> > linesMat;
80
81 /* 创建指向 BinaryDescriptor 对象的指针 */
82 Ptr < BinaryDescriptor > bd = BinaryDescriptor::createBinaryDescriptor();
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 << "无法加载图像。" << std::endl;
97 help();
98 exit( -1 );
99 }
100
101 /* 计算线和描述符 */
102 std::vector < KeyLine > lines;
103 Mat computedDescr;
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 /* 组成查询矩阵 */
113 Mat queries;
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 )
120 queries.push_back( descriptorsMat[j] );
121 }
122
123 std::cout << "已生成一个包含 " << queries.rows << " 个描述符的矩阵" << std::endl;
124
125 /* 创建 BinaryDescriptorMatcher 对象 */
126 Ptr < BinaryDescriptorMatcher > bdm = BinaryDescriptorMatcher::createBinaryDescriptorMatcher();
127
128 /* 填充匹配器 */
129 bdm->add( descriptorsMat );
130
131 /* 计算匹配项 */
132 std::vector < std::vector<DMatch> > matches;
133 bdm->radiusMatch( queries, matches, 30 );
134 std::cout << "匹配样本大小 " << 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 << "匹配:" << matches[i][j].queryIdx << " " << matches[i][j].trainIdx << " " << matches[i][j].distance << std::endl;
141 }
142
143 }
144
145}
146
147#else
148
149int main()
150{
151 std::cerr << "OpenCV 构建时未包含 features2d 模块" << std::endl;
152 return 0;
153}
154
155#endif // HAVE_OPENCV_FEATURES2D
int rows
行数和列数,当矩阵维度超过2维时为(-1, -1)
定义 mat.hpp:2155