• cvHoughLines2霍夫直线检测函数详解及源码解析


    转载请注明出处。  
    文章链接:https://blog.csdn.net/duiwangxiaomi/article/details/126406184
    
    • 1
    • 2

    一. 前言

      霍夫变换是一种在图像中寻找直线、圆及其他简单形状的方法,利用Hough变换在二值图像中找到直线。本文主要介绍opencv自带的几种直线检测函数,以及主要检测函数cvHoughLines2()的源码解析。

    二. cvHoughLines2函数定义

      目前opencv直线检测方法有如下三种:

    1. CV_HOUGH_STANDARD(SHT)
    	传统或标准Hough变换.每一个线段由两个浮点数(ρ,θ)表示,此中ρ是原点(0,0)到直线的距
    	离,θ表示线段与x-轴之间的夹角。是以,矩阵类型必须是 CV_32FC2 type.
    2. CV_HOUGH_PROBABILISTIC(PPHT)
    	概率Hough变换(如果图像包含一些长的线性分割,则效率更高). 它返回线段分割而不是整条
    	直线。每个分割用起点和终点来表示,所以矩阵(或创建的序列)类型是 CV_32SC4.
    3. CV_HOUGH_MULTI_SCALE(MSHT)
    	传统 Hough 变换的多标准变种。线段的编码体式格式与 CV_HOUGH_STANDARD 的一致。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      opencv自带的几种直线检测函数,如下:

    序号使用方式函数名称
    1C接口cvHoughLines2()
    2C++接口-SHT,MSHTHoughLines()
    3C++接口-PPHTHoughLinesP()

    (一) 函数说明

    !!!注意:参数中的theta为检测直线对应的垂线角度,从后面的源码解析可以看出。
    // 1.C接口
    CV_IMPL CvSeq*
    cvHoughLines2( CvArr* src_image, void* lineStorage, int method,
                   double rho, double theta, int threshold,
                   double param1, double param2 )
    函数说明:C接口中的hough检测实现了上述三种检测方法,调用时可通过method设置检测方法。
    返 回 值:返回找到的线段序列.
    参数说明:
    		src_image	输入 8-比特、单通道 (二值) 图像
    		lineStorage	指向保存结果位置的指针,既可以是内存块cvMemoryStorage,
    					也可以是N*1的矩阵数列(行数N将有助于限制直线的最大数量)
    		method		采用的检测方法,可以是
    					CV_HOUGH_STANDARD(SHT)
    					CV_HOUGH_PROBABILISTIC(PPHT)
    					CV_HOUGH_MULTI_SCALE(MSHT)
    		rho			以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的
    					单位半径。一般设置为1
    		theta		以弧度为单位的角度精度.另一种形容方式是直线搜索时的进步尺寸的
    					单位角度.一般设置为CV_PI/180.
    		threshold	累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面
    					中必须达到的值.阈值>threshold的线段才可以被检测通过并返回到
    					结果中.
    		param1		1)对传统 Hough 变换,不使用(0).
    					2)对概率 Hough 变换,它是最小线段长度.x方向或y向有一者距离满足
    					要求即可.
    					3)对多尺度 Hough 变换,它是距离精度rho的分母(大致的距离精度是
    					rho而精确的应该是rho/param1 ).
    		param2		1)对传统 Hough 变换,不使用 (0).
    					2)对概率 Hough 变换,这个参数表示在同一条直线上进行碎线段连接的
    					最大间隔值(gap), 即当同一条直线上的两条碎线段之间的间隔小于
    					param2时,将其合二为一。
    					3)对多尺度 Hough 变换,它是角度精度 theta 的分母 (大致的角度精
    					度是 theta 而精确的角度应该是 theta/param2). 			
    
    • 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
    // 2.C++接口
    // 标准和多尺度霍夫变换函数
    void HoughLines(InputArray image, OutputArray lines, 
    				double rho, double theta, int threshold, 
    				double srn=0, double stn=0 )
    函数说明:此函数实现了标准霍夫变换SHT和多尺度霍夫变换MSHT进行直线检测。
    		调用时可通过method设置检测方法。
    参数说明:
    		image		InputArray类型的image,输入图像,即源图像,需为8位的单通道二进
    					制,可将任意的源图载入进来由函数修改成此格式后,填在此处。
    		lines		OutputArray类型的lines,储存检测到线条的输出矢量.每一条线由
    					(ρ,θ),其中,ρ是离坐标原点((0,0)(也就是图像的左上角)的距离.
    					θ是弧度线条旋转角度(0~垂直线,π/2~水平线).
    		rho			同cvHoughLines2中参数说明
    		theta		同cvHoughLines2中参数说明
    		threshold	同cvHoughLines2中参数说明
    		srn			默认值0
    					对于多尺度霍夫变换,是第三个参数进步尺寸rho的除数距离。
    					粗略的累加器进步尺寸直接是第三个参数rho,而精确的累加器进步尺寸为
    					rho/srn。
    
    		stn			默认值0
    					对于多尺度霍夫变换,srn表示第四个参数进步尺寸的单位角度theta的
    					除数距离。且如果srn和stn同时为0,就表示使用经典的霍夫变换。否则,
    					这两个参数应该都为正数。
    
    // 概率霍夫变换
    void HoughLinesP(InputArray image, OutputArray lines, 
    				double rho, double theta, int threshold, 
    				double minLineLength=0, double maxLineGap=0 )
    函数说明:C++接口将概率霍夫变换单独出来的函数。
    参数说明:
    		image			同HoughLines中参数说明
    		lines			同HoughLines中参数说明
    		rho				同cvHoughLines2中参数说明
    		theta			同cvHoughLines2中参数说明
    		threshold		同cvHoughLines2中参数说明
    		minLineLength	同cvHoughLines2中参数param1-2)说明
    		maxLineGap		同cvHoughLines2中参数param2-2)说明
    
    • 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

    (二) 函数使用

    1. cvHoughLines2示例
    #include 
    #include 
    #include 
     
    int main(int argc, char** argv)
    {
    	IplImage* src;
    	src = cvLoadImage(./001.jpg”, 0 ); //加载灰度图
    	IplImage* dst = cvCreateImage( cvGetSize( src ), IPL_DEPTH_8U, 1 );
    	IplImage* color_dst = cvCreateImage( cvGetSize( src ), IPL_DEPTH_8U, 3 );  //创建三通道图像,用于直线显示
    	CvMemStorage* storage = cvCreateMemStorage(0);
    	CvSeq* lines = 0;
    	
    	cvCanny( src, dst, 50, 100, 3 );  //首先运行边缘检测,得到只有边缘的二值图像
    	lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 80, 30, 10 ); 
    	
     	//提取直线并显示
     	cvCvtColor( dst, color_dst, CV_GRAY2BGR ); 
    	for( int i = 0; i < lines ->total; i++ )  //lines存储的是直线
    	{
    		CvPoint* line = ( CvPoint* )cvGetSeqElem( lines, i );  //lines序列里面存储的是像素点坐标
    		cvLine( color_dst, line[0], line[1], CV_RGB( 0, 255, 0 ) );  //将找到的直线标记为绿色
    	}
    	cvNamedWindow( "src", 1 );
    	cvShowImage( "src", src );
    	cvNamedWindow( "Hough", 1 );
    	cvShowImage( "Hough", color_dst );
    	cvWaitKey(0);
     
    	return 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
    1. 对于HoughLines、HoughLinesP使用,可直接参照毛星云博客,大佬写的非常详细,这里贴上链接。
      毛星云hough直线检测

    三. 源码解析

    (一) HoughLines、HoughLinesP源码

      先贴出HoughLines、HoughLinesP函数源码,可以看出,二者最终都调用了cvHoughLines2函数,因此,我们直接对cvHoughLines2源码进行解析。

    void cv::HoughLines( InputArray _image, OutputArray _lines,
                         double rho, double theta, int threshold,
                         double srn, double stn )
    {
        Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE);
        Mat image = _image.getMat();
        CvMat c_image = image;
        CvSeq* seq = cvHoughLines2( &c_image, storage, srn == 0 && stn == 0 ?
                        CV_HOUGH_STANDARD : CV_HOUGH_MULTI_SCALE,
                        rho, theta, threshold, srn, stn );
        seqToMat(seq, _lines);
    }
    
    void cv::HoughLinesP( InputArray _image, OutputArray _lines,
                          double rho, double theta, int threshold,
                          double minLineLength, double maxGap )
    {
        Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE);
        Mat image = _image.getMat();
        CvMat c_image = image;
        CvSeq* seq = cvHoughLines2( &c_image, storage, CV_HOUGH_PROBABILISTIC,
                        rho, theta, threshold, minLineLength, maxGap );
        seqToMat(seq, _lines);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    (二) cvHoughLines2源码解析

      这里主要对概率霍夫变换和标准变换进行分析,直接上源码,注释在代码中~

    1. cvHoughLines2,method处是代码核心,分别调用了三种检测方法的函数,2、3直接进行标准变换和概率霍夫变换函数的源码解析。
    // cvHoughLines2
    CV_IMPL CvSeq*
    cvHoughLines2( CvArr* src_image, void* lineStorage, int method,
                   double rho, double theta, int threshold,
                   double param1, double param2 )
    {
        CvSeq* result = 0;
    
        CvMat stub, *img = (CvMat*)src_image;
        CvMat* mat = 0;
        CvSeq* lines = 0;
        CvSeq lines_header;
        CvSeqBlock lines_block;
        int lineType, elemSize;
        int linesMax = INT_MAX;
        int iparam1, iparam2;
    
        img = cvGetMat( img, &stub );
    
        if( !CV_IS_MASK_ARR(img))
            CV_Error( CV_StsBadArg, "The source image must be 8-bit, single-channel" );
    
        if( !lineStorage )
            CV_Error( CV_StsNullPtr, "NULL destination" );
    
        if( rho <= 0 || theta <= 0 || threshold <= 0 )
            CV_Error( CV_StsOutOfRange, "rho, theta and threshold must be positive" );
    
        if( method != CV_HOUGH_PROBABILISTIC )
        {
            lineType = CV_32FC2;
            elemSize = sizeof(float)*2;
        }
        else
        {
            lineType = CV_32SC4;
            elemSize = sizeof(int)*4;
        }
    
        if( CV_IS_STORAGE( lineStorage ))
        {
            lines = cvCreateSeq( lineType, sizeof(CvSeq), elemSize, (CvMemStorage*)lineStorage );
        }
        else if( CV_IS_MAT( lineStorage ))
        {
            mat = (CvMat*)lineStorage;
    
            if( !CV_IS_MAT_CONT( mat->type ) || (mat->rows != 1 && mat->cols != 1) )
                CV_Error( CV_StsBadArg,
                "The destination matrix should be continuous and have a single row or a single column" );
    
            if( CV_MAT_TYPE( mat->type ) != lineType )
                CV_Error( CV_StsBadArg,
                "The destination matrix data type is inappropriate, see the manual" );
    
            lines = cvMakeSeqHeaderForArray( lineType, sizeof(CvSeq), elemSize, mat->data.ptr,
                                             mat->rows + mat->cols - 1, &lines_header, &lines_block );
            linesMax = lines->total;
            cvClearSeq( lines );
        }
        else
            CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* nor CvMat*" );
    
        iparam1 = cvRound(param1);
        iparam2 = cvRound(param2);
    
        switch( method )
        {
        case CV_HOUGH_STANDARD:
              icvHoughLinesStandard( img, (float)rho,
                    (float)theta, threshold, lines, linesMax );
              break;
        case CV_HOUGH_MULTI_SCALE:
              icvHoughLinesSDiv( img, (float)rho, (float)theta,
                    threshold, iparam1, iparam2, lines, linesMax );
              break;
        case CV_HOUGH_PROBABILISTIC:
              icvHoughLinesProbabilistic( img, (float)rho, (float)theta,
                    threshold, iparam1, iparam2, lines, linesMax );
              break;
        default:
            CV_Error( CV_StsBadArg, "Unrecognized method id" );
        }
    
        if( mat )
        {
            if( mat->cols > mat->rows )
                mat->cols = lines->total;
            else
                mat->rows = lines->total;
        }
        else
            result = lines;
    
        return result;
    }
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    1. 标准霍夫变换
    static void
    icvHoughLinesStandard( const CvMat* img, float rho, float theta,
                           int threshold, CvSeq *lines, int linesMax )
    {
    	const uchar* image;
    	int step, width, height;
    	int numangle, numrho;
    	int total = 0;
    	int i, j;
    	float irho = 1 / rho;
    	double scale;
    
    	CV_Assert( CV_IS_MAT(img) && CV_MAT_TYPE(img->type) == CV_8UC1 );
    
    	image = img->data.ptr;
    	step = img->step;
    	width = img->cols;
    	height = img->rows;
    
    	numangle = cvRound(CV_PI / theta); //极坐标空间theta轴细分程度
    	numrho = cvRound(((width + height) * 2 + 1) / rho); //极坐标空间rho轴细分程度
                   //实质最小可以取图像两个对角之间的最大距离,eg: M*N的图片最大距离为sqrt(M^2+N^2)
                   //如上计算,显然>sqrt(M^2+N^2), 使得计算分辨率更高
    			   
    	_accum.allocate((numangle+2) * (numrho+2));  //多分配一行一列,主要是方便stage    
    	//2中4邻域的比较,否则比较时会溢出
    	_sort_buf.allocate(numangle * numrho);
    	_tabSin.allocate(numangle);
    	_tabCos.allocate(numangle);
    	int *accum = _accum, *sort_buf = _sort_buf;
    	float *tabSin = _tabSin, *tabCos = _tabCos;
    	
    	memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) );
    	
    	float ang = 0;
    	for(int n = 0; n < numangle; ang += theta, n++ )
    	{
    		tabSin[n] = (float)(sin((double)ang) * irho);    //做好tabSin数组,后面备查
    		tabCos[n] = (float)(cos((double)ang) * irho);    //做好tabCos数组,后面备查
    	}
    	
    	// stage 1. fill accumulator
    	for( i = 0; i < height; i++ )
    		for( j = 0; j < width; j++ )
    		{
    			if( image[i * step + j] != 0 )                //二值图像非零点
    				for(int n = 0; n < numangle; n++ )
    				{
    					int r = cvRound( j * tabCos[n] + i * tabSin[n] );   //ρ = x cos θ + y sin θ
    					r += (numrho - 1) / 2;               //距离偏移一半,r有负值,使r取值在[0,numrho - 1]区间
    					accum[(n+1) * (numrho+2) + r+1]++;   //累加器相应单元+1,
    														//n+1是为了第一行空出来
    														//numrho+2 是总共的列数
    														//r+1把第一列空出来,stage 2需要比较4邻域累加器中值的大小
    				}
    		}
    	
    	// stage 2. find local maximums
    	for(int r = 0; r < numrho; r++ )
    		for(int n = 0; n < numangle; n++ )
    		{
    			int base = (n+1) * (numrho+2) + r+1;         //累加器空间的索引,与stage 1中相同
    			if( accum[base] > threshold &&
    				accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] &&
    				accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] )
    				sort_buf[total++] = base;
    		}
    	
    	// stage 3. sort the detected lines by accumulator value
    	icvHoughSortDescent32s( sort_buf, total, accum );    //opencv自带排序函数,
    							//降序排列,降序排列后的数据在accum中的序号赋给sort_buf
    	
    	// stage 4. store the first min(total,linesMax) lines to the output buffer
    	linesMax = MIN(linesMax, total);
    	scale = 1./(numrho+2);
    	for( i = 0; i < linesMax; i++ )
    	{
    		CvLinePolar line;
    		int idx = sort_buf[i];   //累加器空间accum的序号
    		int n = cvFloor(idx*scale) - 1;   //cvFloor()将浮点数转换为不大于该参数的整数
    										//除以(numrho + 2)并减1→获得行数
    		int r = idx - (n+1)*(numrho+2) - 1;  //获得列数
    		line.rho = (r - (numrho - 1)*0.5f) * rho;   //0.5=1/2,距离大小,与之前偏移相对应
    		line.angle = n * theta;        //角度大小
    		cvSeqPush( lines, &line );     //直线以(ρ,r)装到lines中
    	}
    
    }
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    1. 概率霍夫变换
    // icvHoughLinesProbabilistic
    static void
    icvHoughLinesProbabalistic( CvMat* image,
                                float rho, float theta, int threshold,
                                int lineLength, int lineGap,
                                CvSeq *lines, int linesMax )
    {
    		CvMat* accum = 0;//累加器
    		CvMat* mask = 0;//保存0,1图像
    		CvMat* trigtab = 0;//保存cos、sin与距离精度(irho)的乘积
        CvMemStorage* storage = 0;
     
        CV_FUNCNAME( "icvHoughLinesProbalistic" );
     
        __BEGIN__;
        
        CvSeq* seq;
        CvSeqWriter writer;
        int width, height;
        int numangle, numrho;
        float ang;
        int r, n, count;
        CvPoint pt;
        float irho = 1 / rho;
        CvRNG rng = cvRNG(-1);//产生随机数
        const float* ttab;
        uchar* mdata0;
     
        CV_ASSERT( CV_IS_MAT(image) && CV_MAT_TYPE(image->type) == CV_8UC1 );
     
        width = image->cols;
        height = image->rows;
     
        numangle = cvRound(CV_PI / theta);
        numrho = cvRound(((width + height) * 2 + 1) / rho);
     
        CV_CALL( accum = cvCreateMat( numangle, numrho, CV_32SC1 ));
        CV_CALL( mask = cvCreateMat( height, width, CV_8UC1 ));
        CV_CALL( trigtab = cvCreateMat( 1, numangle, CV_32FC2 ));
        cvZero( accum );
        
        CV_CALL( storage = cvCreateMemStorage(0) );
        
        for( ang = 0, n = 0; n < numangle; ang += theta, n++ )
        {
            trigtab->data.fl[n*2] = (float)(cos(ang) * irho);
            trigtab->data.fl[n*2+1] = (float)(sin(ang) * irho);
        }
        ttab = trigtab->data.fl;
        mdata0 = mask->data.ptr;
     
        CV_CALL( cvStartWriteSeq( CV_32SC2, sizeof(CvSeq), sizeof(CvPoint), storage, &writer )); 
     
        //第一步生成0,1图像,即:选择非零的点
        // stage 1. collect non-zero image points
        for( pt.y = 0, count = 0; pt.y < height; pt.y++ )
        {
            const uchar* data = image->data.ptr + pt.y*image->step;
            uchar* mdata = mdata0 + pt.y*width;
            for( pt.x = 0; pt.x < width; pt.x++ )
            {
                if( data[pt.x] )
                {
                    mdata[pt.x] = (uchar)1;
                    CV_WRITE_SEQ_ELEM( pt, writer );//存入链表
                }
                else
                    mdata[pt.x] = 0;
            }
        }
     
        seq = cvEndWriteSeq( &writer );
        count = seq->total;
     
    		//随机处理
        // stage 2. process all the points in random order
        for( ; count > 0; count-- )
        {
    		    // choose random point out of the remaining ones
    				int idx = cvRandInt(&rng) % count;//生成随机数
    				int max_val = threshold-1, max_n = 0;
    				CvPoint* pt = (CvPoint*)cvGetSeqElem( seq, idx );
    				CvPoint line_end[2] = {{0,0}, {0,0}};
    				float a, b;
    				int* adata = accum->data.i;
    				int i, j, k, x0, y0, dx0, dy0, xflag;
    				int good_line;
    				const int shift = 16;
    		
    				i = pt->y;
    				j = pt->x;
     
    				//注意这行代码是为了覆盖pt指向的内容,也就是说pt指向的链表seq的内容被count-1位置上的内容覆盖了
            // "remove" it by overriding it with the last element
            *pt = *(CvPoint*)cvGetSeqElem( seq, count-1 );
     
            // check if it has been excluded already (i.e. belongs to some other line)
            if( !mdata0[i*width + j] )
                continue;
                
    				//更新 累加器,查找最大概率的线
            // update accumulator, find the most probable line
            for( n = 0; n < numangle; n++, adata += numrho )
            {
                r = cvRound( j * ttab[n*2] + i * ttab[n*2+1] );
                r += (numrho - 1) / 2;//r有负值,使r取值在[0,numrho - 1]区间
                int val = ++adata[r];
                if( max_val < val )
                {
                    max_val = val;
                    max_n = n;
                }
            }
     
    				//如果点的个数max_val < threshold 就被认为是不符合条件的候选点(i,j)
            // if it is too "weak" candidate, continue with another point
            if( max_val < threshold )
                continue;
     
    				//如果点的个数max_val >= threshold 就被认为是符合条件的候选点(i,j)
    			  // from the current point walk in each direction
    			  // along the found line and extract the line segment
    				//极坐标中的方向角是直线的垂线与极轴正向的夹角,在图像中夹角是第四象限的角
    				//(极轴正向逆时针旋转,极轴就是在平面直角坐标系中的x轴正方向,对于图像来说,y轴正向是向下的)
    				//所以sin取负值,cos不变
    					a = -ttab[max_n*2+1];
    					b = ttab[max_n*2];
    					x0 = j;
    					y0 = i;
    				
    				//计算步长dx0,dy0
            if( fabs(a) > fabs(b) )
            {
                xflag = 1;
                dx0 = a > 0 ? 1 : -1;
                dy0 = cvRound( b*(1 << shift)/fabs(a) );
                y0 = (y0 << shift) + (1 << (shift-1));
    	    			//1 << shift这是为了把浮点数计算转化为整数计算
            }
            else
            {
                xflag = 0;
                dy0 = b > 0 ? 1 : -1;
                dx0 = cvRound( a*(1 << shift)/fabs(b) );
                x0 = (x0 << shift) + (1 << (shift-1));
            }
     
    				//当点的位置和cos、sin确定后,每条直线都有两个方向
            for( k = 0; k < 2; k++ )
            {
                int gap = 0, x = x0, y = y0, dx = dx0, dy = dy0;
                
                if( k > 0 ) //控制两个方向(正好相反)
                    dx = -dx, dy = -dy;
     
                // walk along the line using fixed-point arithmetics,
                // stop at the image border or in case of too big gap
                for( ;; x += dx, y += dy )
                {
                    uchar* mdata;
                    int i1, j1;
     
                    if( xflag )
                    {
                        j1 = x;
                        i1 = y >> shift;
                    }
                    else
                    {
                        j1 = x >> shift;
                        i1 = y;
                    }
     
                    if( j1 < 0 || j1 >= width || i1 < 0 || i1 >= height )
                        break;
     
                    mdata = mdata0 + i1*width + j1;
     
                    // for each non-zero point:
                    //    update line end,
                    //    clear the mask element
                    //    reset the gap
                    if( *mdata )
                    {
    									gap = 0;
    									line_end[k].y = i1;
    									line_end[k].x = j1;
                    }
                    else if( ++gap > lineGap )//像素间隙大于lineGap 则退出
                        break;
                }
            }
     
    				//分别计算X、Y方向距离
            good_line = abs(line_end[1].x - line_end[0].x) >= lineLength ||
                        abs(line_end[1].y - line_end[0].y) >= lineLength;
     
            for( k = 0; k < 2; k++ )
            {
                int x = x0, y = y0, dx = dx0, dy = dy0;
                
                if( k > 0 )
                    dx = -dx, dy = -dy;
     
                // walk along the line using fixed-point arithmetics,
                // stop at the image border or in case of too big gap
                for( ;; x += dx, y += dy )
                {
                    uchar* mdata;
                    int i1, j1;
     
                    if( xflag )
                    {
                        j1 = x;
                        i1 = y >> shift;
                    }
                    else
                    {
    									j1 = x >> shift;
    									i1 = y;
    								}
     
                    mdata = mdata0 + i1*width + j1;
     
                    // for each non-zero point:
                    //    update line end,
                    //    clear the mask element
                    //    reset the gap
                    //如果*mdata == 1则设置为0,去除已经检测过的点
                    if( *mdata )
                    {
                    		//如果是直线,则去除累加器里面的值
                        if( good_line )
                        {
                            adata = accum->data.i;
                            for( n = 0; n < numangle; n++, adata += numrho )
                            {
                                r = cvRound( j1 * ttab[n*2] + i1 * ttab[n*2+1] );
                                r += (numrho - 1) / 2;//r有负值,使r取值在[0,numrho - 1]区间
                                adata[r]--;
                            }
                        }
                        *mdata = 0;
                    }
     
                    if( i1 == line_end[k].y && j1 == line_end[k].x )
                        break;
                }
            }
     
            if( good_line )
            {
                CvRect lr = { line_end[0].x, line_end[0].y, line_end[1].x, line_end[1].y };
                cvSeqPush( lines, &lr );
                if( lines->total >= linesMax )
                    EXIT;
            }
        }
     
        __END__;
     
        cvReleaseMat( &accum );
        cvReleaseMat( &mask );
        cvReleaseMat( &trigtab );
        cvReleaseMemStorage( &storage );
    }
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266

    四. 总结

      今天的博客就到这里啦,欢迎大家在评论区互相学习讨论,我们下期见,三连哦~

    参考链接:
    1.【OpenCV入门教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑
    2. openCV cvHoughLines2 函数源码解析(CV_HOUGH_PROBABILISTIC 基于概率的霍夫变换)
    3. 第六章 - 图像变换 - 霍夫线变换(cvHoughLines2)

  • 相关阅读:
    75. 颜色分类(中等 数组 双指针 排序)
    单例 模式
    【ROS入门】机器人导航(仿真)——导航实现
    二、ROS2基本操作
    善于利用GPT确实可以解决许多难题
    【无标题】
    Qt自己画个Y轴,如何和qcustomplot建立的X轴原点对齐?
    计算机毕业设计ssm+vue基本微信小程序的拼车自助服务小程序
    x64 番外篇——知识铺垫
    R的seurat和python的scanpy对比学习
  • 原文地址:https://blog.csdn.net/duiwangxiaomi/article/details/126406184