线特征算法检测简介
LSD(Line Segment Detector)直线检测分割算法,在图像梯度变化明显的区域来检测局部直线的轮廓,因此也称作直线分割。LSD算法在输入图像预处理进行下采样,目的在于降低图像中出现的锯齿效应。通过计算图像中的梯度幅值进行梯度排序,(边缘区域梯度幅值较大),然后通过区域增长算法来进行线特征的检测。具体算法细节可参考:LSD论文。LSD算法优点:①线性时间内检测出亚像素的精度;②无需任何参数调节;③能够控制误检直线数量;
OpenCV代码线特征匹配:
// line_match.cpp : 定义控制台应用程序的入口点。
//
#include <iostream>
#include <opencv2/opencv_modules.hpp>
#ifdef HAVE_OPENCV_FEATURES2D
#include <opencv2/line_descriptor.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/highgui.hpp>
#define MATCHES_DIST_THRESHOLD 25
using namespace cv;
using namespace cv::line_descriptor;
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;
}
int main(int argc, char** argv)
{
String image_path1 = "..\\line_match\\image\\sbasketball1.png";
String image_path2 = "..\\line_match\\image\\sbasketball2.png";
if (image_path1.empty() || image_path2.empty())
{
help();
return -1;
}
/* 加载图片 */
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 path" << std::endl;
}
/* create binary masks */
cv::Mat mask1 = Mat::ones(imageMat1.size(), CV_8UC1);
cv::Mat mask2 = Mat::ones(imageMat2.size(), CV_8UC1);
/* BinaryDescriptor指针默认参数 */
Ptr<BinaryDescriptor> bd = BinaryDescriptor::createBinaryDescriptor();
/* 计算线段与描述子 */
std::vector<KeyLine> keylines1, keylines2;
cv::Mat descr1, descr2;
(*bd)(imageMat1, mask1, keylines1, descr1, false, false);
(*bd)(imageMat2, mask2, keylines2, descr2, false, false);
/* 选择第一塔关键线段进行描述 */
std::vector<KeyLine> lbd_octave1, lbd_octave2;
Mat left_lbd, right_lbd;
for (int i = 0; i < (int)keylines1.size(); i++)
{
if (keylines1[i].octave == 0)
{
lbd_octave1.push_back(keylines1[i]);
left_lbd.push_back(descr1.row(i));
}
}
for (int j = 0; j < (int)keylines2.size(); j++)
{
if (keylines2[j].octave == 0)
{
lbd_octave2.push_back(keylines2[j]);
right_lbd.push_back(descr2.row(j));
}
}
/* BinaryDescriptorMatcher二值描述子匹配创建 */
Ptr<BinaryDescriptorMatcher> bdm = BinaryDescriptorMatcher::createBinaryDescriptorMatcher();
/* 匹配 */
std::vector<DMatch> matches;
bdm->match(left_lbd, right_lbd, matches);
/* 筛选高精度匹配点对 */
std::vector<DMatch> good_matches;
for (int i = 0; i < (int)matches.size(); i++)
{
if (matches[i].distance < MATCHES_DIST_THRESHOLD)
good_matches.push_back(matches[i]);
}
/* 画出匹配对 */
cv::Mat outImg;
cv::Mat scaled1, scaled2;
std::vector<char> mask(matches.size(), 1);
drawLineMatches(imageMat1, lbd_octave1, imageMat2, lbd_octave2, good_matches, outImg,
Scalar::all(-1), Scalar::all(-1), mask, DrawLinesMatchesFlags::DEFAULT);
std::cout << "BinaryDescriptorMatcher is : " << good_matches.size() << std::endl;
imshow("Matches", outImg);
/* LSD 检测 */
Ptr<LSDDetector> lsd = LSDDetector::createLSDDetector();
/* 检测线段 */
std::vector<KeyLine> klsd1, klsd2;
Mat lsd_descr1, lsd_descr2;
// lsd->detect(image, klsd, scale, numOcatives, mask) numOcatives = 2
lsd->detect(imageMat1, klsd1, 2, 2, mask1);
lsd->detect(imageMat2, klsd2, 2, 2, mask2);
/* 计算尺度第一塔的描述 */
bd->compute(imageMat1, klsd1, lsd_descr1);
bd->compute(imageMat2, klsd2, lsd_descr2);
/* 尺度第一塔进行特征与描述提取 */
std::vector<KeyLine> octave0_1, octave0_2;
Mat leftDEscr, rightDescr;
for (int i = 0; i < (int)klsd1.size(); i++)
{
if (klsd1[i].octave == 1)
{
octave0_1.push_back(klsd1[i]);
leftDEscr.push_back(lsd_descr1.row(i));
}
}
for (int j = 0; j < (int)klsd2.size(); j++)
{
if (klsd2[j].octave == 1)
{
octave0_2.push_back(klsd2[j]);
rightDescr.push_back(lsd_descr2.row(j));
}
}
/* 匹配点对 */
std::vector<DMatch> lsd_matches;
bdm->match(leftDEscr, rightDescr, lsd_matches);
/* 选择高精度匹配点对 */
good_matches.clear();
for (int i = 0; i < (int)lsd_matches.size(); i++)
{
if (lsd_matches[i].distance < MATCHES_DIST_THRESHOLD)
good_matches.push_back(lsd_matches[i]);
}
/* 画出匹配点对 */
cv::Mat lsd_outImg;
resize(imageMat1, imageMat1, Size(imageMat1.cols/2, imageMat1.rows/2));
resize(imageMat2, imageMat2, Size(imageMat2.cols/2, imageMat2.rows/2));
std::vector<char> lsd_mask(matches.size(), 1);
drawLineMatches(imageMat1, octave0_1, imageMat2, octave0_2, good_matches, lsd_outImg,
Scalar::all(-1), Scalar::all(-1), lsd_mask, DrawLinesMatchesFlags::DEFAULT);
imshow("LSD matches", lsd_outImg);
std::cout << "LSDescriptorMatcher is : " << good_matches.size() << std::endl;
imwrite("..\\line_match\\image\\matches.jpg", outImg);
imwrite("..\\line_match\\image\\lsd_matches.jpg", lsd_outImg);
waitKey(0);
return 0;
}
#else
int main()
{
std::cerr << "OpenCV was built without features2d module" << std::endl;
return 0;
}
#endif // HAVE_OPENCV_FEATURES2D
线特征匹配实验结果:
默认二值描述符线检测与LSD线检测匹配性能对比:
![](https://i-blog.csdnimg.cn/blog_migrate/4582e96ce46009b1ff6ee1ef9d8e7fb6.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/a9cd43fd5729613a34650c9e14106792.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/e205e9d6821591c8407c013685f1aeec.png)
总结
由于刚刚接触线匹配算法,还没有研究线特征匹配的源码,后期会阅读解析LSD直线检测与对应线特征描述符进行最近邻匹配算法。目前只是简单跑一下线特征匹配算法demo程序,最后还是那句话:如有错误,还请批评指正!
参考文献:
http://www.cvvision.cn/tag/lsd/
https://docs.opencv.org/4.0.0-beta/examples.html
http://ubee.enseeiht.fr/vision/ELSD/
https://www.computer.org/csdl/trans/tp/2010/04/ttp2010040722.html