线特征算法检测简介

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线检测匹配性能对比:

二值描述符线检测匹配算法(左) LSD线检测匹配算法(右)
二值描述符线检测匹配与LSD线检测匹配点对
总结

由于刚刚接触线匹配算法,还没有研究线特征匹配的源码,后期会阅读解析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

GitHub 加速计划 / opencv31 / opencv
77.37 K
55.71 K
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:2 个月前 )
c3747a68 Added Universal Windows Package build to CI. 2 天前
9b635da5 - 2 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐