opencv 九种直线检测方法汇总

文章目录

  • 1、场景需求
  • 2、Hough_line直线检测算法
    • 2.1 Hough_line实现步骤
    • 2.2 Hough_line代码实战
    • 2.3 效果展示与分析
    • 2.4 HoughP_line代码实战
    • 2.5 效果展示与分析
  • 3、LSD直线检测算法
    • 3.1 LSD算法简介
  • 3.2 LSD代码实战
    • 3.3 效果展示与分析
  • 4、FLD直线检测算法
    • 4.1 FLD算法简介
    • 4.2 FLD算法代码实战
    • 4.3 效果展示与分析
    • 5.1 EDlines算法简介
    • 5.2 EDlines算法实现步骤
    • 6.2 LSWMS算法代码实现
    • 6.3 效果展示与分析
  • 7、CannyLines直线检测算法
    • 7.1 CannyLines算法简介
    • 7.2 CannyLines算法代码实战
    • 7.3 效果展示与分析
  • 8、MCMLSD直线检测算法
    • 8.1 MCMLSD算法简介
    • 8.2 MCMLSD算法代码实战
    • 8.3 效果展示与分析
  • 9、LSM直线检测算法
    • 9.1 LSM算法简介
    • 9.3 效果展示与分析
  • 参考资料
  • 注意事项

1、场景需求

在计算机视觉领域,我们经常需要做一些特殊的任务,而这些任务中经常会用到直线检测算法,比如车道线检测、长度测量等。尽管直线检测的任务看起来比较简单,但是在具体的应用过程中,你会发现这里面还是有很大的优化空间,本文对常用的一些比较经典的直线检测算法进行汇总。

2、Hough_line直线检测算法

Hough变换是一个比较有名的计算机视觉处理算法,该算法可以用来做很多的任务,常用的任务包括直线检测、圆检测、椭圆检测等,下面我们将对该算法进行简单的分析并进行代码实战。

2.1 Hough_line实现步骤

步骤1-首先,它创建一个二维数组或累加器(用于保存两个参数的值),并将其初始设置为零;
步骤2-用r来表示行,用θ来表示列;
步骤3-数组的大小取决于你所需要的精度。假设您希望角度的精度为1度,则需要180列(直线的最大度数为180);
步骤4-对于r,可能的最大距离是图像的对角线长度。因此,取一个像素精度,行数可以是图像的对角线长度。

2.2 Hough_line代码实战

# coding=utf-8
# 导入相应的python包
import cv2 
import numpy as np # 读取输入图片
img = cv2.imread('test3.jpg') 
# 将彩色图片灰度化
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 
# 使用Canny边缘检测 
edges = cv2.Canny(gray,50,200,apertureSize = 3) 
# 进行Hough_line直线检测
lines = cv2.HoughLines(edges,1,np.pi/180, 200) 
print(lines)
# 遍历每一个r和theta
for i in range(len(lines)):r,theta = lines[i, 0, 0], lines[i, 0, 1]# 存储cos(theta)的值a = np.cos(theta)# 存储sin(theta)的值b = np.sin(theta) # 存储rcos(theta)的值x0 = a*r # 存储rsin(theta)的值 y0 = b*r  # 存储(rcos(theta)-1000sin(theta))的值x1 = int(x0 + 1000*(-b)) # 存储(rsin(theta)+1000cos(theta))的值y1 = int(y0 + 1000*(a)) # 存储(rcos(theta)+1000sin(theta))的值x2 = int(x0 - 1000*(-b)) # 存储(rsin(theta)-1000cos(theta))的值y2 = int(y0 - 1000*(a))  # 绘制直线结果  cv2.line(img,(x1,y1), (x2,y2), (0,255,0),2) 
# 保存结果
cv2.imwrite('test3_r.jpg', img) 
cv2.imshow("result", img)
cv2.waitKey(0)

2.3 效果展示与分析

在这里插入图片描述
上图展示了一些Hough_line算法的直线检测效果。为了验证该算法的有效性,我选择了3个不同的具有挑战性的场景,建筑物、答题卡和门。通过观察上面的结果我们可以知道该算法基本上能够检测出图中的直线,但是检测的结果并不是很好,有很多重复和漏检的情况。除此之外,该算法最大的缺点就是需要根据图片去调节参数,关键的参数是lines = cv2.HoughLines(edges,1,np.pi/180, 200) 中的200,该值设置的越大图片中检测出来的直线数量会越少,你需要根据你自己的测试场景进行调节。

2.4 HoughP_line代码实战

HoughP_line是Hough_line算法的改进版,具有更快的速度和更好的效果。

# coding=utf-8
# 导入相应的python包
import cv2 
import numpy as np # 读取输入图片
img = cv2.imread('test3.jpg') 
# 将彩色图片灰度化
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 
# 使用Canny边缘检测 
edges = cv2.Canny(gray,50,200,apertureSize = 3) 
# 进行Hough_line直线检测
lines = cv2.HoughLinesP(edges,1,np.pi/180, 80, 30, 10) # 遍历每一条直线
for i in range(len(lines)): cv2.line(img,(lines[i, 0, 0],lines[i, 0, 1]), (lines[i, 0, 2],lines[i, 0, 3]), (0,255,0),2) 
# 保存结果
cv2.imwrite('test3_r.jpg', img) 
cv2.imshow("result", img)
cv2.waitKey(0)

2.5 效果展示与分析

在这里插入图片描述
上图展示了一些HoughP_line算法的直线检测效果。上图都是使用lines = cv2.HoughLinesP(edges,1,np.pi/180, 80, 30, 10) 这个固定的参数来计算出结果的,通过观察我们可以发现,HoughLinesP不仅使用起来比较方便,基本上不需要进行调节参数;除此之外,该算法能够获得更好的直线检测效果。因此,当你想用Hough直线检测算法,建议你优先使用HoughP_line算法。

3、LSD直线检测算法

LSD-项目主页-论文链接-代码链接
  LSD是opencv中集成的一个直线检测算法,该算法的直线检测效果优于Hough算法,而且具有较好的检测速度,推荐使用。

3.1 LSD算法简介

LSD快速直线检测算法是由Rafael Grompone、Jeremie Jackbowicz、Jean-Michel Morel于2010年发表在PAMI上的文献《LSD:a Line Segment Dectctor》中提出的,该算法时间复杂度较霍夫变换低。LSD算法通过对图像局部分析,得出直线的像素点集,再通过假设参数进行验证求解,将像素点集合与误差控制集合合并,进而自适应控制误检的数量 。 一般来说,要检测图像中的直线,最基本的思想是检测图像中梯度变化较大的像素点集,LSD算法也正是利用梯度信息和行列线(level-line)来进行直线检测的。

3.2 LSD代码实战

# coding=utf-8
import cv2
import numpy as np# 读取输入图片
img0 = cv2.imread("test3.jpg")
# 将彩色图片转换为灰度图片
img = cv2.cvtColor(img0,cv2.COLOR_BGR2GRAY)# 创建一个LSD对象
lsd = cv2.createLineSegmentDetector(0)
# 执行检测结果
dlines = lsd.detect(img)
# 绘制检测结果
for dline in dlines[0]:x0 = int(round(dline[0][0]))y0 = int(round(dline[0][1]))x1 = int(round(dline[0][2]))y1 = int(round(dline[0][3]))cv2.line(img0, (x0, y0), (x1,y1), (0,255,0), 1, cv2.LINE_AA)# 显示并保存结果
cv2.imwrite('test3_r.jpg', img0)
cv2.imshow("LSD", img0)
cv2.waitKey(0)
cv2.destroyAllWindows()

3.3 效果展示与分析

在这里插入图片描述

上图展示了一些LSD算法的直线检测效果。通过观察上面的结果,我们可以发现该算法的检测结果远远优于Hough和HoughP算法;除此之外,上述的检测结果都是使用LSD算法的默认参数进行执行,如果针对特定的参数进行调节,可以取得更好的结果,这种情况一般是在你的特定需求场景中对默认的一些参数进行微调操作,往往能获得意想不到的结果。

在这里插入图片描述
上图展示了LSD直线检测算法的一些超参数,具体的细节请看该链接。需要说明的是该算法具有3中不同的模式,具体的模式如下图所示,默认情况下使用模式2(即1),但是经过测试我发现模式1(即0)通常输出的效果会更好一些,具体的情况需要你根据你的场景进行分析。

4、FLD直线检测算法

4.1 FLD算法简介

FLD直线检测算法是在该论文中被引入的,该论文中尝试着使用直线特征来代替原始的SURF点特征进行建筑物识别。与点特征进行相比,线特征具有更容易发现和更好的鲁棒性,线特征基本上不会受到光照、遮挡、视角变化的影响。下面展示了该算法的直线检测效果,从图中我们可以看出,线特征比点特征更好一些。
在这里插入图片描述

4.2 FLD算法代码实战

# coding=utf-8
import cv2
import numpy as np# 读取输入图片
img0 = cv2.imread("test3.jpg")
# 将彩色图片转换为灰度图片
img = cv2.cvtColor(img0,cv2.COLOR_BGR2GRAY)# 创建一个LSD对象
fld = cv2.ximgproc.createFastLineDetector()
# 执行检测结果
dlines = fld.detect(img)
# 绘制检测结果
# drawn_img = fld.drawSegments(img0,dlines, )
for dline in dlines:x0 = int(round(dline[0][0]))y0 = int(round(dline[0][1]))x1 = int(round(dline[0][2]))y1 = int(round(dline[0][3]))cv2.line(img0, (x0, y0), (x1,y1), (0,255,0), 1, cv2.LINE_AA)# 显示并保存结果
cv2.imwrite('test3_r.jpg', img0)
cv2.imshow("LSD", img0)
cv2.waitKey(0)
cv2.destroyAllWindows()

4.3 效果展示与分析

在这里插入图片描述
上图展示了FLD直线检测算法的检测效果。通过观察我们可以发现该算法具有很好的检测效果,基本上可以检测出图中所有的直线,和LSD的性能类似,具体使用哪种算法需要你根据自己的应用场景去进行选择。

5、EDlines直线检测算法
EDlines-论文链接-代码链接

5.1 EDlines算法简介

Dlines直线检测算法是在该论文中提出的。本文提出了一个快速、无参数的线段检测器,命名为EDLines (Akinlar and Topal, 2011),它产生强大的和准确的结果,比最快的已知线段检测器速度更快,达到11倍;换句话说,the LSD by Grompone von Gioi et al. (2008a,b, 2010). 我们的探测器还包括一个线的验证步骤定于亥姆霍兹原理Helmholtz principle (Desolneux et al., 2008),这让它控制错误检测的数量。 EDLines得到的结果,我们看到的是,LSD非常相似,有所有主要的线段检测,并有极少数误报。此外, EDLines运行实时以炫目的速度为9.45毫秒,约10倍的速度比LSD对给定的图像。

5.2 EDlines算法实现步骤

步骤1-首先,给定一个灰度图像,运行新的边缘检测、边缘绘制(ED)算法,产生一套干净的,像素相邻的链,我们称之为边缘。边缘线段直观地反应对象的边界。
步骤2-然后,利用直线度准则,即最小二乘直线拟合法,从生成的像素链中提取线段。
步骤3-最后,线的验证步骤定于亥姆霍兹原理Helmholtz principle (Desolneux et al., 2008; Grompone von Gioi et al.,2008a)是用来消除虚假线段的检测。

5.3 EDlines算法代码实战
下面仅仅展示了主函数的代码,具体的代码请在网盘链接中下载。

#include "EDLib.h"
#include <iostream>using namespace cv;
using namespace std;int main()
{	//***************************** ED Edge Segment Detection *****************************//Detection of edge segments from an input image	string img_name = "test3.jpg";Mat testImg = imread(img_name, 0);// imshow("Source Image", testImg);//Call ED constructorED testED = ED(testImg, SOBEL_OPERATOR, 36, 8, 1, 10, 1.0, true); // apply ED algorithm//Show resulting edge imageMat edgeImg = testED.getEdgeImage();//imshow("Edge Image - PRESS ANY KEY TO CONTINUE", edgeImg);//waitKey();//Output number of segmentsint noSegments = testED.getSegmentNo();std::cout << "Number of edge segments: " << noSegments << std::endl;//Get edges in segment form (getSortedSegments() gives segments sorted w.r.t. legnths) std::vector< std::vector<Point> > segments = testED.getSegments();//***************************** EDLINES Line Segment Detection *****************************//Detection of line segments from the same imageEDLines testEDLines = EDLines(testImg);Mat lineImg = testEDLines.getLineImage();	//draws on an empty imageimwrite("test3_r.jpg", lineImg);// imshow("Line Image 1 - PRESS ANY KEY TO CONTINUE", lineImg);//Detection of lines segments from edge segments instead of input image//Therefore, redundant detection of edge segmens can be avoidedtestEDLines = EDLines(testED);lineImg = testEDLines.drawOnImage();	//draws on the input imageimwrite("test3_a.jpg", lineImg);imshow("Line Image 2  - PRESS ANY KEY TO CONTINUE", lineImg);//Acquiring line information, i.e. start & end pointsvector<LS> lines = testEDLines.getLines();int noLines = testEDLines.getLinesNo();std::cout << "Number of line segments: " << noLines << std::endl;waitKey();

5.4 效果展示与分析
在这里插入图片描述
上图展示了EDlines直线检测算法的检测效果。通过上面的观察,我们可以发现:1)该算法能获得和LSD类似的检测结果;2)该算法抑制了一部分小的误检的直线;3)该算法具有更快的运行速度,是LSD的10倍左右。

6、LSWMS直线检测算法
LSWMS-论文链接-代码链接

6.1 LSWMS算法简介
 LSWMS是一个直线检测算法。本文介绍了一种精确且实时的直线检测方法。以前的直线检测方法都没有使用到图像场景的先验知识,因而不需要对输入的参数进行微调。该算法在检测精度和检测速度之间进行了折中,文中使用了一个高效的采样方法来进行加速处理,然后,文中使用一种快速的直线增长算法基于bresenham算法用改进的mean-shift算法提供精确的线段,同时保持稳健对抗噪音。测试了该策略的性能对于各种各样的图像,将其结果与流行的最新线段检测方法。结果表明,我们的建议优于这些工作同时考虑了结果和处理速度。

6.2 LSWMS算法代码实现

/** Project:  lineSegments (LSWMS Line Segment using Weighted Mean-Shift)** File:     main.cpp** Contents: Creation, initialisation and usage of LSWMS object*           for the detection of line segments in images or videos** Author:   Marcos Nieto <marcos.nieto.doncel@gmail.com>** Homepage: www.marcosnieto.net*/#ifdef WIN32#include <windows.h>#include <time.h>
#endif#ifdef linux#include <stdio.h>#include <sys/time.h>#include <time.h>
#endif#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/videoio.hpp> 
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>#include "LSWMS.h"using namespace std;
using namespace cv;// Timing
#ifdef WIN32double t1, t2;	
#elseint t1, t2;	struct timeval ts;
#endif
double t;void help()
{cout << "/*\n"<< " **************************************************************************************************\n"<< " * Line segment detection using WMS \n"<< " * ----------------------------------------------------\n"		 << " * \n"<< " * Author:Marcos Nieto\n"<< " * www.marcosnieto.net\n"<< " * marcos.nieto.doncel@gmail.com\n"<< " * \n"<< " * Date:01/04/2012\n"<< " **************************************************************************************************\n"<< " * \n"<< " * Usage: \n"		 		 << " *		-video		# Specifies video file as input (if not specified, camera is used) \n"<< " *		-image		# Specifies image file as input (if not specified, camera is used) \n"<< " *		-verbose	# Actives verbose: ON, OFF (default)\n"<< " *		-play		# ON: the video runs until the end; OFF: frame by frame (key press event)\n"<< " *		-resizedWidth	# Specifies the desired width of the image (the height is computed to keep aspect ratio)\n"<< " *		-numMaxLSegs	# Specifies the maximum number of line segments to detected.\n"		 << " *\n"<< " * 	-hough		# ON: Applies OpenCV HoughLinesP for comparison (PPHT)\n"<< " *\n"<< " * Example:\n"<< " *		lineSegment -video myVideo.avi -verbose ON -play OFF\n"<< " *		lineSegment -image myImage.jpg -resizedWidth 300 -numMaxLSegs 100\n"<< " *		lineSegment -image myImage.jpg -hough ON\n"<< " * \n"<< " * Keys:\n"<< " *		Esc: Quit\n"<< " */\n" << endl;
}
void processPPHT(cv::Mat &img, std::vector<LSEG> &lSegs)
{cv::Mat imgGRAY;cv::cvtColor(img, imgGRAY, CV_RGB2GRAY);cv::Mat dst;cv::Canny(imgGRAY, dst, 20, 80, 3);std::vector<cv::Vec4i> lines;cv::HoughLinesP(dst, lines, 1, CV_PI/180, 80, 30, 10);LSEG lSeg;cv::Point p1, p2;lSegs.clear();for(size_t i=0; i<lines.size(); i++){lSeg.clear();p1.x = lines[i][0];p1.y = lines[i][1];p2.x = lines[i][2];p2.y = lines[i][3];lSeg.push_back(p1);lSeg.push_back(p2);lSegs.push_back(lSeg);			}	
}
void drawPPHT(cv::Mat &dst, std::vector<LSEG> &lSegs, cv::Scalar color)
{for(size_t i=0; i<lSegs.size(); i++){		cv::line(dst, lSegs[i][0], lSegs[i][1], color, 2);	}
}
/** Main function*/
int main(int argc, char** argv)
{	// Imagescv::Mat inputImg, imgGRAY;	cv::Mat outputImg, outputImgPPHT;int procWidth=0, procHeight=0;cv::Size procSize;// Other variableschar *videoFileName = 0;char *imageFileName = 0;cv::VideoCapture video;bool useCamera = true;bool playMode = true;bool stillImage = false;bool verbose = false;int numMaxLSegs = 0;	bool usePPHT = false;// Line segments (LSWMS and PPHT)std::vector<LSEG> lSegs, lSegsPPHT;std::vector<double> errors;// Start showing helphelp();// Parse argumentsif(argc < 2)return -1;	for(int i=1; i<argc; i++){const char* s = argv[i];if(strcmp(s, "-video" ) == 0){// Input video is a video filevideoFileName = argv[++i];useCamera = false;}else if(strcmp(s,"-hough") == 0){const char* ss = argv[++i];if(strcmp(ss, "ON") == 0 || strcmp(ss, "on") == 0|| strcmp(ss, "TRUE") == 0 || strcmp(ss, "true") == 0 || strcmp(ss, "YES") == 0 || strcmp(ss, "yes") == 0 )usePPHT = true;	}else if(strcmp(s,"-image") == 0){// Input is a image fileimageFileName = argv[++i];stillImage = true;useCamera = false;}		else if(strcmp(s, "-numMaxLSegs") == 0){numMaxLSegs = atoi(argv[++i]);	}		else if(strcmp(s, "-resizedWidth") == 0){procWidth = atoi(argv[++i]);}else if(strcmp(s, "-verbose" ) == 0){const char* ss = argv[++i];if(strcmp(ss, "ON") == 0 || strcmp(ss, "on") == 0 || strcmp(ss, "TRUE") == 0 || strcmp(ss, "true") == 0 || strcmp(ss, "YES") == 0 || strcmp(ss, "yes") == 0 )verbose = true;			}else if(strcmp(s, "-play" ) == 0){const char* ss = argv[++i];if(strcmp(ss, "OFF") == 0 || strcmp(ss, "off") == 0 || strcmp(ss, "FALSE") == 0 || strcmp(ss, "false") == 0 || strcmp(ss, "NO") == 0 || strcmp(ss, "no") == 0 || strcmp(ss, "STEP") == 0 || strcmp(ss, "step") == 0)playMode = false;			}		}// Open video inputif( useCamera )video.open(0);else{if(!stillImage)video.open(videoFileName);}// Check video inputint width = 0, height = 0, fps = 0, fourcc = 0;if(!stillImage){if( !video.isOpened() ){printf("ERROR: can not open camera or video file\n");return -1;}else{// Show video informationwidth = (int) video.get(CAP_PROP_FRAME_WIDTH);height = (int) video.get(CAP_PROP_FRAME_HEIGHT);fps = (int) video.get(CAP_PROP_FPS);fourcc = (int) video.get(CAP_PROP_FOURCC);if(!useCamera)printf("Input video: (%d x %d) at %d fps, fourcc = %d\n", width, height, fps, fourcc);elseprintf("Input camera: (%d x %d) at %d fps\n", width, height, fps);}}else{inputImg = cv::imread(imageFileName);if(inputImg.empty())return -1;width = inputImg.cols;height = inputImg.rows;printf("Input image: %s, Size (%d x %d)\n", imageFileName, width, height);playMode = false;}// Resize	if(procWidth != 0){	procHeight = (int)(height*((double)procWidth/width));procSize = cv::Size(procWidth, procHeight);printf("Resize to: (%d x %d)\n", procWidth, procHeight);	}elseprocSize = cv::Size(width, height);if(numMaxLSegs != 0) printf("NumMaxLSegs=%d\n", numMaxLSegs);// ---------------------------// Create and init LSWMSint R = 3;LSWMS lswms(procSize, R, numMaxLSegs, verbose);if(numMaxLSegs==0)printf("LSWMS object created: R=%d\n\n", R);elseprintf("LSWMS object created: R=%d, numMaxLSegs=%d\n\n", R, numMaxLSegs);// ---------------------------// MAIN LOOPint frameNum=0;for( ;; ){if(!stillImage){//if(verbose) printf("\n-------------------------\nFRAME #%6d\n", frameNum);frameNum++;// Get current image		video >> inputImg;}	else{printf("-------------------------\n");}	if( inputImg.empty() )break;// Resize to processing sizecv::resize(inputImg, inputImg, procSize);		// Color Conversionif(inputImg.channels() == 3){cv::cvtColor(inputImg, imgGRAY, CV_BGR2GRAY);	inputImg.copyTo(outputImg);if(usePPHT)inputImg.copyTo(outputImgPPHT);			}else{inputImg.copyTo(imgGRAY);cv::cvtColor(inputImg, outputImg, CV_GRAY2BGR);if(usePPHT)cv::cvtColor(inputImg, outputImgPPHT, CV_GRAY2BGR);			}// ++++++++++++++++++++++++++++++++++++++++// Process LSWMS#ifdef WIN32t1 = ::GetTickCount();#elsegettimeofday(&ts,0);t1 = (ts.tv_sec * 1000 + (ts.tv_usec / 1000));#endiflswms.run(inputImg, lSegs, errors);				#ifdef WIN32t2 = ::GetTickCount();#elsegettimeofday(&ts,0);t2 = (ts.tv_sec * 1000 + (ts.tv_usec / 1000));#endif	// process time = t2 - t1		t = (double)t2-(double)t1;cv::Scalar mean, stddev;cv::meanStdDev(errors, mean, stddev);if(!stillImage)printf("Fr.#%d - LSWMS: %d lines / %.0f ms , Ang.Error: (Mean, Std)=(%.2f, %.2f)(deg)\n", frameNum, lSegs.size(), t, mean.val[0]*180/CV_PI, stddev.val[0]*180/CV_PI);elseprintf("LSWMS: %d segments\nAngular Error: Mean = %.2f (deg), Std = %.2f (deg)\nProcess Time = %.0f (ms)\n", lSegs.size(), mean.val[0]*180/CV_PI, stddev.val[0]*180/CV_PI,  t);//lswms.drawLSegs(outputImg, lSegs,CV_RGB(255,0,0), 2);			// drawing all line segments the samelswms.drawLSegs(outputImg, lSegs, errors);				// drawing according to errors// ++++++++++++++++++++++++++++++++++++++++				// ++++++++++++++++++++++++++++++++++++++++				if(usePPHT){// Process PPHT#ifdef WIN32t1 = ::GetTickCount();#elsegettimeofday(&ts,0);t1 = (ts.tv_sec * 1000 + (ts.tv_usec / 1000));#endifprocessPPHT(inputImg, lSegsPPHT);				#ifdef WIN32t2 = ::GetTickCount();#elsegettimeofday(&ts,0);t2 = (ts.tv_sec * 1000 + (ts.tv_usec / 1000));#endif// process time = t2 - t1		t = (double)t2-(double)t1;drawPPHT(outputImgPPHT, lSegsPPHT, CV_RGB(0,0,255));if(!stillImage)printf("Fr.#%d - PPHT: %d lines / %.0f ms\n", frameNum, lSegsPPHT.size(), t);elseprintf("\nPPHT: %d segments\nProcess Time = %.0f (ms)\n", lSegsPPHT.size(), t);// ++++++++++++++++++++++++++++++++++++++++	}// Viewimshow("LSWMS", outputImg);	if(usePPHT)imshow("PPHT", outputImgPPHT);if(stillImage){cv::imwrite("lswms.bmp", outputImg);if(usePPHT)cv::imwrite("ppht.bmp", outputImgPPHT);}if(playMode)cv::waitKey(1);elsecv::waitKey(0);char q = (char)waitKey(1);if( q == 27 ){printf("\nStopped by user request\n");break;}	if(stillImage)break;} // main whileif(!stillImage)video.release();return 0;}

6.3 效果展示与分析

在这里插入图片描述
上图展示的是LSWMS论文中的结果。由于作者提供的代码需要在Ubuntu下编译,我没有去做这个工作,如果你感兴趣你可以去运行在本文的3张测试图片上面的效果。

7、CannyLines直线检测算法

CannyLines-项目主页-论文链接 -代码链接

7.1 CannyLines算法简介

CannyLines算法在该论文中被提出。本文提出了一种鲁棒的线段检测算法,有效地检测出输入图像中的线段。**首先,提出了一种无参数canny算子cannypf,通过自适应地设置传统canny算子的低阈值和高阈值,从输入图像中稳健地提取边缘映射。其次,提出了直接从边缘地图中提取共线点簇的有效像素连接和分割技术,并基于最小二乘法对初始线段进行拟合。第三,通过有效的扩展和合并,生成更长、更完整的线段。最后,根据helmholtz原理对检测到的所有线段进行了验证,该原理同时考虑了梯度方向和幅度信息。**在一组有代表性的图像上的实验结果表明,与常用的两种线段检测器lsd和edline相比,我们提出的cannyline线段检测器能够提取出更有意义的线段,特别是在人造场景中。

7.2 CannyLines算法代码实战

这里仅仅展示了主函数,完整的代码请从网盘链接进行下载。

#include <stdio.h>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/types_c.h>#include "CannyLine.h"using namespace cv;
using namespace std;void main()
{	string fileCur = "test3.jpg";cv::Mat img = imread( fileCur, 0 );cv::Mat img1 = imread(fileCur, 1);CannyLine detector;std::vector<std::vector<float> > lines;detector.cannyLine( img, lines );// showcv::Mat imgShow( img.rows, img.cols, CV_8UC3, cv::Scalar( 255, 255, 255 ) );for ( int m=0; m<lines.size(); ++m ){cv::line( img1, cv::Point( lines[m][0], lines[m][1] ), cv::Point( lines[m][2], lines[m][3] ), cv::Scalar(0,255,0), 1, LINE_AA );}imshow("",img1);imwrite("test3_r.jpg", img1);cv::waitKey(0);
}

7.3 效果展示与分析

在这里插入图片描述
上图展示了CannyLines直线检测算法的检测效果。通过观察上图我们可以发现该算法能够获得更加准确的检测结果,具有更少的误检。该算法主要是提高了边缘检测的效果,具体的效果如下所示:
在这里插入图片描述

8、MCMLSD直线检测算法

MCMLSD-论文链接-代码链接

8.1 MCMLSD算法简介

MCMLSD算法出自于该论文。论文中提出了一个融合两者优点的概率算法。在第一阶段,使用全局概率hough方法。在第二阶段,在图像域中分析每个检测到的行,以定位在霍夫图中生成峰值的线段。通过将搜索限制在一条直线上,线段的分布线上的点序列可以被建模为马尔可夫链和概率最优标签使用标准动态规划算法精确计算,在线性时间内。马尔可夫假设同时也产生了一种直观的排名方法,它使用估计期望值的边际后验概率线段上正确标记的点的数目。评估由此产生的马尔可夫链边缘线段检测器(mcmlsd)我们开发并应用了一种新的定量方法控制分段不足和分段过量的评估方法。在YorkUrbanDB数据集的评价结果表明,所提出的MCMLSD方法优于以相当大的优势达到最先进水平。

8.2 MCMLSD算法代码实战

这里仅仅展示了主函数,完整的代码请从网盘链接进行下载。

clear
clc
close alladdpath(genpath('code/'));
addpath('Img2/');
img = imread(['Img2', filesep, 'test3.jpg']);
% img = imresize(img, [300, 400]);
% img = imresize(img, [round(size(img,1)/4), round(size(img,2)/4)]);%compute the kernel for the image size
%you only need to compute the kernal once for one an image size
[kernels, kernels_flip, kernel_params] =kernelInitialization(img);
ticId = tic;
%the lines variable contains the detected line segmentations it arranged as
%[x1 y1 x2 y2 probability]
%The fullLines are the detected lines. It is arranged as [rho theta probability]
[lines, fullLines] =lineSegmentation_HighRes(img,kernels, kernels_flip, kernel_params);
display('Total time');
toc(ticId)
fig = figure;
imshow(img);
hold all%Order lines by probabilitylines = sortrows(lines, -5);ttlLines = size(lines,1);for i = 1:ttlLines%plot the top 90 linesline([lines(i,1) lines(i,3)], [lines(i,2) lines(i,4)],'Color', rand(1,3), 'LineWidth', 3);end

8.3 效果展示与分析

在这里插入图片描述
上图展示了MCMLSD算法的检测效果。通过上面的观察,我们可以发现该算法取得了很好的检测效果,但是美中不足的是该算法的运行速度比较慢,可能也和matlab代码有关吧。

9、LSM直线检测算法

LSM-项目主页-论文链接-代码链接

9.1 LSM算法简介

LSM算法不仅仅是一个直线检测算法,同时也是一个直线合并算法。论文中提出了一种合并这些断开的线段的算法,以恢复原始的感知准确的线段。该算法根据角度和空间接近度对线段进行分组。然后将每组中满足新的自适应合并准则的线段对依次合并成一条线段。重复此过程,直到不再合并行段。我们还提出了一种定量比较线段检测算法的方法。在york-urban数据集上的结果表明,与最新的线段检测算法相比,我们的合并线段更接近人类标记的地面真线段。

9.2 LSM算法代码实战
这里仅仅展示了主函数,完整的代码请从网盘链接进行下载。

% 清除空间变量
clear;
close all;
clc;
addpath(genpath(pwd));%****THRESHOLD AND PARAMETER SETTING***%
% 设置一些参数和阈值
xi_s=.05;   % spatial proximity threshold
tau_theta=pi/24; % angular proximity threshold% 设置保存结果的名字
img_name = 'img1\test3.jpg';
name = split(img_name, '\');
save_name1 = ['result1\ori_', char(name(2))];
save_name2 = ['result1\opt_', char(name(2))];
save_name3 = ['result1\merge_', char(name(2))];
% 读取输入图片
I=imread(img_name);
% I=rgb2gray(I);
I=double(I);
D=lsd(I/max(I(:))*255);
[L]=mergeLines(D,tau_theta,xi_s);
display(strcat(int2str(size(L,1)),' merged line segments'));
imgD=double(display_image( D,I));
imgL=double(display_image( L,I));imwrite([imgD],save_name1);
imwrite([imgL],save_name2);
imwrite([imgL],save_name3);imshow([imgD imgL]);

9.3 效果展示与分析

在这里插入图片描述
上图展示了直线检测算法LSM的检测效果。左边一列表示的是LSD的检测结果,右边一列表示的是LSM算法的优化效果,图中不同的颜色表示不同的直线。通过上面的观察,我们可以发现LSM可以将一些间断的直线合并成一条更长的直线,这在现实场景中具有很大的用处,但是我们也会发现LSM算法会错误的将一些直线进行合并,会造成一些误差。

10、总结
本文对比较经典的直线检测算法进行了总结和分析。对于直线检测这个任务而言,它在现实场景中具有很多的应用,对于一个具体的场景,你可以根据自己的需要从本文中选择出一个合适的直线检测算法进行应用,如果你的场景比较简单,HoughP_line算法可能就可以满足你的要求;如果你同时对速度和精度有要求,可以选择使用EDlines等;如果你需要获得尽可能长的直线,那么建议你使用LSM直线检测算法。总而言之,最适合你的场景的算法才是最好的算法。
在这里插入图片描述

参考资料

[1] LSD
[2] EDLines
[3] LSWMS
[4] CannyLines
[5] MCMLSD
[6] LSM

注意事项

[1] 如果您对AI、自动驾驶、AR、ChatGPT等技术感兴趣,欢迎关注我的微信公众号“AI产品汇”,有问题可以在公众号中私聊我!
[2] 该博客是本人原创博客,如果您对该博客感兴趣,想要转载该博客,请与我联系(qq邮箱:1575262785@qq.com),我会在第一时间回复大家,谢谢大家的关注.
[3] 由于个人能力有限,该博客可能存在很多的问题,希望大家能够提出改进意见。
[4] 如果您在阅读本博客时遇到不理解的地方,希望您可以联系我,我会及时的回复您,和您交流想法和意见,谢谢。
[5] 本文测试的图片可以通过关注微信公众号AI产品汇之后向我索取!

九种直线检测方法汇总

LSD直线检测优缺点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/60422.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LWN:移除kthread freezer!

关注了就能看到更多这么棒的文章哦&#xff5e; Removing the kthread freezer By Jake EdgeJune 23, 2023LSFMMBPFChatGPT assisted translationhttps://lwn.net/Articles/935602/ 2023 年 Linux 存储、文件系统、内存管理和 BPF 峰会的最后一天&#xff0c;由 Luis Chamberla…

Google 后开发的 Carbon 真的会取代 C++ 吗?

【CSDN 编者按】Carbon 真的能成为 C 的替代品吗&#xff1f;目前我们只能猜测&#xff0c;但请不要忘记 Swift 或 Kotlin 等语言的诞生。 原文链接&#xff1a;https://semaphoreci.com/blog/carbon 未经授权&#xff0c;禁止转载&#xff01; 作者 | Manuel Rubio 译者 | 弯…

email邮箱注册,5分钟教会你这些方法大全

不同的人&#xff0c;根据需求不同&#xff0c;可以注册诸如免费邮箱、VIP邮箱、企业邮箱等等。我们可以通过邮箱品牌之一的TOM 邮箱为例&#xff0c;5分钟教会大家如何注册这3类邮箱。 一、尊贵VIP邮箱注册方法&#xff1a; VIP邮箱账号注册可支持3-5位超短靓号&#xff0c;…

电子邮箱免费注册,比较好用的电子邮箱怎么注册?如何申请?

电子邮箱免费的很多&#xff0c;我们常用的163、TOM、QQ等&#xff0c;如果公司用&#xff0c;就要用企业电子邮箱了。申请企业电子邮箱&#xff0c;注册3年用6年&#xff0c;注册5年用10年&#xff0c;这是在网上看到TOM企业邮箱的优惠&#xff0c;以下是企业邮箱总结。 TOM企…

个人邮箱怎么注册呢?个人邮箱申请能绑定微信收发邮件吗?

个人邮箱怎么注册呢&#xff1f;个人邮箱申请能绑定微信收发邮件吗&#xff1f; 大多数职场人都在偷偷的努力着 学习新的方法&#xff0c;通过更高效的方式完成工作&#xff01; 在新的时代下&#xff0c;唯一不变的就是瞬息万变 脑部了一万种功成名就的场景&#xff0c;不…

2022还不知道登陆邮箱账号怎么填写?个人邮箱登录注册流程看详解

今年入冬以来&#xff0c;我国多地散发新冠肺炎疫情。浙江三地同日报告新增、满洲里大规模核酸共检出阳性431例、上海新增本土1例。“奥密克戎”以惊人的速度传播&#xff0c;目前已至少在38个国家和地区出现。 疫情在即不仅需要配合日常防疫工作还要做好个人的防护&#xff0…

免费注册 Proton.me电子邮件教程

免费注册 Proton.me 1.登录自己的 Protonmail 帐户。 2.访问此页面&#xff0c;单击 “转到设置” 选项&#xff0c;在左侧边栏中&#xff0c;选择 “身份和地址”。 3.会在此页面顶部看到 proton.me 字样。 4.单击 “激活用户名 proton.me ” 按钮。 5.申请成功后&#xff…

Python 简单编写一个注册邮箱

金秋九月&#xff0c;又是开学的新的一学期&#xff0c;大家见到新同学新同事一定要问好&#xff0c;不要社死!!! 我们步入正题: 编写一个简单的注册邮箱程序 我在编写这个小程序的时候里面&#xff0c;有的代码是简化了: 还是那句话: 编写程序前&#xff0c;先理清思路&#x…

测试用例:邮箱注册

思维导图&#xff1a; 测试用例&#xff1a; 用例编号分类所属等价类测试描述输入数据预期结果实际结果测试人员测试时间000001邮件地址有效6-18个字符&#xff0c;可使用字母、数字、下划线&#xff0c;须以字母开头Jianghe_123456可用提示&#xff1a;恭喜&#xff0c;该邮件…

Python启蒙——检测邮箱是否有新邮件,并邮件通知(二)

承接上回&#xff0c;知道了新邮件返回的消息后&#xff0c;可以更近一步&#xff0c;来实现发邮件。 发邮件我们要用到SMTP的服务&#xff0c;来发送邮件。 发送邮件一般有收件人地址、抄送人地址、邮件主题、邮件内容等内容&#xff0c;从SMTPLIB这个类的解释和例子如下&am…

教育邮箱怎么注册申请,教育电子邮箱注册小妙招

临近秋季开学很多小伙伴咨询学校邮箱的问题&#xff0c;学校是否会给学生配备邮箱&#xff0c;学校都用什么邮箱&#xff1f; 最近我的妹妹刚拿到录取通知书&#xff0c;里面写着让她办理一个教育邮箱&#xff0c;她不太知道什么是教育邮箱&#xff0c;还给教务处的招生处老师…

大话Stable-Diffusion-Webui-将stable-diffusion-webui接入微信群(一)

文章目录 效果原理WeChatRobot安装克隆项目安装pc端微信安装 WeChatRobot配置 WeChatRobot接入sd的文生图apisd配置WeChatRobot中接入sdconfig.yaml增加sd的配置robot.py开发代码robot.pyconfig.yaml效果 原理 通过一个玩转微信的开源项目

AI智能机器人,在这里也可以体验~

“ 大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 ” 现在是&#xff1a;2023年2月17日00:14:42 前言 最近AI智能chatgpt特别的火&#xff0c;相信好多人都已经体验过了&#xff0c;之前我的群里也接入过&#xff0c;奈何总是收到警告和…

python中成语接龙游戏_Python实现成语接龙

这是一篇用Python实现成语接龙小游戏的具体开发教程。Python实现这个功能非常容易,以下分为两个版本,一个是简易版,能够实现基本的功能。还有一个是拓展版,是在简易版上进行拓展,功能更为复杂且完善。 这个可以应用在很多方面,比如说聊天机器人,而且还可以结合itchat这个…

AI绘画结合GPT 把Ai绘画与摄影玩明白

一、绘画与摄影有什么关系&#xff1f; 绘画和摄影是两种不同的艺术形式&#xff0c;它们都以其自身独特的方式捕捉和表达现实。在某些方面&#xff0c;它们是相互联系的&#xff0c;而在其他方面&#xff0c;它们又有所不同。​ 相似之处&#xff1a;绘画和摄影都是创造性的…

电脑用户名中文改英文步骤总结

背景&#xff1a;在单位领了两台新电脑&#xff0c;脑子一热用户名直接设置成中文名字了&#xff0c;后来在装许多环境的时候都出现中文乱码、路径错误等情况&#xff0c;因此需要改成英文账户名。 步骤&#xff1a; 先修改本地的账户名。打开控制面板并点击用户账户&#xf…

「ChatCat」创建你的专属机器人

没有OpenAI账号&#xff0c;不能科学上网&#xff0c;还没找到合适工具的朋友可以看过来。 【ChatCat】可以创建自己的应用&#xff0c;自己使用/供其他人使用&#xff1b;也可以简单粗暴直接使用官方ChatGPT&#xff1b;还可以生成山水图配文案&#xff0c;你想怎么用&#x…

找了程序员男朋友,她们是这样报喜的...

最近收到好多女生在面向对象脱单的消息&#xff0c;继续安利面向对象给还单着的盆友们。如果你身边有单着的朋友或同事&#xff0c;请帮忙推荐哦&#xff5e; 还不了解面向对象&#xff1f; 面向对象是一个真实、高效的相亲平台&#xff0c;为单身青年服务。目前&#xff0c;人…

当你交了一个程序员男朋友后,会变成什么样……

当你交了一个程序员男朋友后&#xff0c;会变成什么样…… 程序员&#xff0c;是一种职业&#xff0c;但由于网络上关于程序员的段子络绎不绝&#xff0c;导致这一职业在大家心中也有了不一样的颜色。 比如&#xff0c;很多人会认为和程序员交往一定是件特别的事情&#xff0…