2016-10-21 23:08:12 Yogi_zhang 阅读数 1743

一、原理

 参考霍夫变换,如果有课本的话参考冈萨雷斯的图像处理课本及matlab版本的课本最好,如果没有就看下这节的原理描述。
直角坐标系中对于直线方程有如下等式:

ρ=xcosθ+ysinθ(ρ0)

其中ρ为坐标原点到直线的距离,θρ 与x轴正方向的夹角,x、y为直线上的点:
如图
对于直线上任意一个已知的点(x0,y0)有:ρ=x0cosθ+y0sinθ,则该函数是关于ρθ的方程。

所以,对于图像上的任意一个像素点都有上述方程成立,亦即对于图像上的任意一个点,在霍夫变换后的参数空间内(θ,ρ)都可以找到一条三角函数曲线与其对应。图像上的任意两个点,即对应两条曲线,那么这两条曲线必然会产生一个交点(θ0,ρ0),这个交点再放到直线方程中,即可确定一条直线(也正是图像上这两个点所确立的直线)。

由此推断,图像上在同一条直线(图像上应该是一个线段)上的点所产生的曲线都会在参数空间相交于一个点(θ0,ρ0),直线的长度越长(点越多),那么交于该点的曲线也越多。

所以,在图像上找直线可以演变成在霍夫变化后参数空间(霍夫图像)中找曲线相交次数最多的点。

代码

平台:Ti的DSP系列
语言:C
2011-11-18 20:54:01 liujia2100 阅读数 13453

一天从网上下了20个vc的hough代码,没有一个代码是成功的。令人郁闷,我参考matlab代码写出了hough检测单个直线的程序

Hough变换:

本程序是我花费时间最长的程序;参考matlab算法;首先求出原图上的每一个像素在变换域上的对应曲线,即原图上每一点(i,j)对应变换域曲线(p,k);p=(int)(i*cos(pi*k/180)+j*sin(pi*k/180))(0<k<180);

然后遍历(i,j),累加变换域重复出现的位置,即曲线的交点

然后遍历累加器,寻找交点最大的数据,找出对应的原图坐标,即为所求直线

主要代码

for(i=0;i<img.height;i++) //转化成灰度

{

for(j=0,n=0;n<img.width*3,j<img.width;n+=3,j++)

{ //gray 变量存储rgb转灰度的数据

gray= ((float)(img.image[lineBytes*i+n+2])+(float)(*(img.image+lineBytes*i+n+1))+(float)(*(img.image+lineBytes*i+n)))/3; //lineBytes 原图每行实际字节数

grayPic[i*img.width+j]=(byte)gray;//转换成的灰度图像放在grayPic中

}

}

int logNum; //边缘检测

memset(lpDIBBits,(byte)0,sizeof(byte)*img.height*img.width);

for(i=3;i<img.height-2;i++)

for(j=3;j<img.width-2;j++)

{

//logNum 变量 记录每次运算的值

logNum=16*grayPic[i*img.width+j]-grayPic[(i-2)*img.width+j]-grayPic[(i-1)*img.width+j-1]-2*grayPic[(i-1)*img.width+j]-grayPic[(i-1)*img.width+j+1]-grayPic[i*img.width+j-2]-2*grayPic[i*img.width+j-1]-2*grayPic[i*img.width+j+1]-grayPic[i*img.width+j+2]-grayPic[(i+1)*img.width+j-1]-2*grayPic[(i+1)*img.width+j]-grayPic[(i+1)*img.width+j+1]-grayPic[(i+2)*img.width+j];//log算子

if(logNum > 0)

lpDIBBits[i*img.width+j]=255;//边缘检测后的数据存放在lpDIBBits中

else

lpDIBBits[i*img.width+j]=0;

}

for(i=1;i<img.height;i++) //img.height原图高度

for(j=1;j<img.width;j++) //img.width 原图宽度

{

if(lpDIBBits[i*img.width+j]==255) //对边缘检测后的数据(存在lpDIBBits中)进行hough变化

{

for(k=1;k<ma;k++) //ma=180

{

p=(int)(i*cos(pi*k/180)+j*sin(pi*k/180));//p hough变换中距离参数

p=(int)(p/2+mp/2); //p值优化防止为负

npp[k][p]=npp[k][p]++; //npp对变换域中对应重复出现的点累加

}

}

}

kmax=0; //最长直线的角度

pmax=0; //最长直线的距离

n=0; //这一部分为寻找最长直线

for(i=1;i<ma;i++) //ma=180

for(j=1;j<mp;j++) //mp为原图对角线距离

{

if(npp[i][j]>yuzhi) //找出最长直线 yuzhi为中间变量用于比较

{

yuzhi=npp[i][j];

kmax=i; //记录最长直线的角度

pmax=j; //记录最长直线的距离

}

}

memset(temp,(byte)255,sizeof(byte)*img.width*img.height);//原图中坐标符合kmax和pmax的值

for(i=1;i<img.height;i++) //的集合即是最长的直线

for(j=1;j<img.width;j++)

{

if(lpDIBBits[i*img.width+j]==255)

{

p=(int)(i*cos(pi*kmax/180)+j*sin(pi*kmax/180));//pi=3.1415926

p=(int)(p/2+mp/2); //mp为原图对角线距离

if(p==pmax)

*(temp+i*img.width+j)=0; //存储图像数据 放在temp数组中

}

}


右边是原图,里面有圆和直线,左边是经过hough变换检测出的直线(版权所有 欢迎交流)

2017-04-28 08:02:02 xieyan0811 阅读数 651

1.      用途

Hough变换是一种在图像中寻找直线,圆及其它简单形状的方法.当我们对图像进行边缘检测之后,可用Hough变换识别图像中的简单形状.该转换也是对图像的一种抽象(由繁到简).下面介绍最基本Hough变换:寻找直线算法.

2.      思路

Hough变换通过从直角坐标系到极坐标系的转换,将直角坐标系中的一条"直线",转换为极坐标系上的一个"点",落在这条"直线"上的像素点越多,这个极坐标中"点"的权越重,最终通过分析各个"点"的权重(局部最大值),获取重要线段.
为区别直角坐标系中的点和极坐标系中的点,下面我们将直角坐标系中的点称为像素点.

3.      具体实现


如图所示,假设我们有一个桃心图形,由多个红色像素点组成(红色为其有意义的像素点,即轮廓值),想提取出其中的直线.
如果将各个像素点连成直线,直线将会有很多条,有些有意义,有些没意义.一般认为像素点足够多的直线更具意义,比如图中的绿色和蓝色的直线.
在直角坐标系中,可以用斜截式y=kx+b来表示一条直线(k是斜率,b是y轴上的截距).
转换到极坐标系P(ρ,θ);ρ称为P点的极径(从原点到直线的垂直距离,上图中的虚线),θ称为P点的极角(极径与x轴的夹角).
直角坐标系中的一条"直线",可转换为极坐标系上的一个"点"(上图中的黑圈),也就是说:把直角坐标系中的多个像素点,对应成极坐标系中的一个点.落在该"直线"上的像素点越多,极坐标系中的该点的权重越大,通过比较权重,取得重要直线.

4.      总结

Hough变换通过映射,将一个形状识别问题,转换成了一个统计问题,在寻找直线的过程中,将落入一条直线上的点映射成了极径和极角(二元),在寻找圆的过程中,将落入圆弧上的点映射成了圆心点坐标x,y与半径r的组合(三元),通过映射简化了数据描述,映射后的数据也可以作为图像的一种抽象特征进行其它运算.
可其扩展到识别更复杂的形状,加入颜色更一步判断,以及利用基本形状之间的关系,层层抽象,组合出更加复杂的功能.

2019-11-10 21:37:50 qq_41498261 阅读数 50

在图像的处理中,会遇到一种情况,根据给定的点集(比如轮廓)拟合出一条直线的情景。

1.最小二乘法拟合直线

最小二乘法多项式直线拟合,根据给定的点,求出它的函数 y=f(x)y=f(x),当然求得准确的函数是不太可能的,但是我们能够求出它的近似曲线 y=φ(x)y=φ(x)

原理:
假如有点I = 1,2,3,……n,求近似曲线 y=φ(x)y=φ(x),并且使得 y=φ(x)y=φ(x)y=f(x)y=f(x)的平方偏差和最小偏差:

现在有点(x1,y1x_1,y_1),(x2,y2x_2,y_2)···(xn,ynx_n,y_n)
设: 拟合多项式为:y=ax+by=ax+b
平方偏差和如下:
e2=i=1n(yiy)2=i=1n(yi(ax+b))2e^2=\sum_{i=1}^{n}(y_i-y)^2 =\sum_{i=1}^{n}(y_i-(ax+b))^2

其中我们要找到一组最好的a和b,“最好的”就是要使选出的a b能使得所有的误差达到最小化。所以上面得到的e2=i=1n(yi(ax+b))2e^2=\sum_{i=1}^{n}(y_i-(ax+b))^2就是一个关于a和b的函数。

2.多元函数极值与最值问题的理论依据

二元函数取极值的必要条件(类比一元函数)。

可以看到最小二乘法对各个变量求偏导,使得偏导值为0,即可得到最小值,因为e是关于a、b的函数,导数为0的点必定是最小值。
分别对a、b求偏导:
在这里插入图片描述
在这里插入图片描述

OpenCV中的API

  • 说明
    使线拟合2D或者3D点集。

    函数fitLine通过最小化iρ(ri)\sum_i \rho(r_i)将线拟合到2维或3维点,其中rir_i是第 ithi^{th}个点之间的距离,该线和ρ(r)\rho(r)是一个距离函数。

    该算法基于M-estimator技术,该技术使用加权最小二乘算法迭代拟合直线。每次迭代后,权重wiw_i被调整ρ(ri)\rho(r_i)

  • 声明

    void fitLine(
    		 InputArray points, 
    		 OutputArray line, 
    		 int distType,
    		 double param, 
    		 double reps, 
    		 double aeps 
     );
    
  • 参数

    points 用于拟合直线的输入点集,可以是2维或者3维点向量。保存在std::vector<>或者Mat中。
    line 输出的直线。对于二维直线而言,类型为cv::Vec4f;对于三维直线类型,则为cv::Vec6f。输出参数的前半部分给出的是直线的方向,而后半部分给出的是直线上的一点(即通常所说的点斜式直线)。
    distType 距离类型。拟合直线时,要使输入点到拟合直线的距离和最小化,可供选择的距离类型如下,rir_i表示的是输入的点到直线的距离。
    param 某些距离类型的数值参数(C)。跟所选的距离类型有关,值可以设置为0,cv::fitLine()函数本身会自动选择最优化的值如果为0。
    reps 半径的足够精度(坐标原点和直线之间的距离)。
    aeps 角度精度足够。对于reps和aeps,0.01将是一个很好的默认值。
    distType类型

    ① DIST_L2
    ρ(r)=r2/2(the simplest and the fastest least-squares method) \rho(r) = r^2/2 \quad \text{(the simplest and the fastest least-squares method)}
    ② DIST_L1
    ρ(r)=r\rho (r) = r
    ③ DIST_L12
    ρ(r)=2(1+r221)\rho (r) = 2 \cdot ( \sqrt{1 + \frac{r^2}{2}} - 1)
    ④ DIST_FAIR
    ρ(r)=C2(rClog(1+rC))whereC=1.3998\rho \left (r \right ) = C^2 \cdot \left ( \frac{r}{C} - \log{\left(1 + \frac{r}{C}\right)} \right ) \quad \text{where} \quad C=1.3998
    ⑤ DIST_WELSCH
    ρ(r)=C22(1exp((rC)2))whereC=2.9846\rho \left (r \right ) = \frac{C^2}{2} \cdot \left ( 1 - \exp{\left(-\left(\frac{r}{C}\right)^2\right)} \right ) \quad \text{where} \quad C=2.9846
    ⑥ DIST_HUBER
    ρ(r)={r2/2if  r<CC(rC/2)otherwisewhereC=1.345\rho (r)= \begin{cases} r^2/2 &if \ \ r < C \\ C \cdot (r-C/2) &otherwise \end{cases} \quad \text{where} \quad C=1.345

应用

void apprixiLine() {
	//创建一个用于绘图的空白图
	Mat src = Mat::zeros(480, 640, CV_8UC3);

	//输入需要拟合的点
	vector<Point> points;
	points.push_back(Point(48, 58));
	points.push_back(Point(105, 98));
	points.push_back(Point(155, 160));
	points.push_back(Point(212, 220));
	points.push_back(Point(248, 260));
	points.push_back(Point(320, 300));
	points.push_back(Point(350, 360));
	points.push_back(Point(412, 400));

	//将拟合点画到空白画板上
	for (size_t i = 0; i < points.size(); i++)
	{
		circle(src, points[i], 5, Scalar(0, 0, 255));
	}
	imshow("src", src);

	Vec4f line_para;
	fitLine(points, line_para, DIST_L2, 0, 1e-2, 1e-2);
	cout << "line_para: " << line_para << endl;

	Point point0;
	point0.x = line_para[2];
	point0.y = line_para[3];
	cout << "point0:[" << point0.x << "," << point0.y << "]" << endl;

	double k = line_para[1] / line_para[0];
	cout << "k=" << k << endl;

	//计算直线的端点
	Point point1, point2;
	point1.x = 0;
	point1.y = k * (0 - point0.x) + point0.y;
	point2.x = 640;
	point2.y = k * (640 - point0.x) + point0.y;
	cout << "point1:[" << point1.x << "," << point1.y << "]" << endl;
	cout << "point2:[" << point2.x << "," << point2.y << "]" << endl;
    

	line(src, point1, point2, Scalar(0, 255, 0), 2);

	imshow("dst", src);
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 事例二
void findFitLine(Mat& src) {
	//1. 转化灰度图像
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);

	//2. 二值化
	Mat binary;
	threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);

	//3. 获得二维点集
	vector<Point> point_para;
	Point point_temp;
	for (size_t i = 0; i < src.rows; i++)
	{
		for (size_t j = 0; j < src.cols; j++)
		{
			if (binary.at<unsigned char>(i, j) < 255) {
				point_temp.x = j;
				point_temp.y = i;
				point_para.push_back(point_temp);
			}
		}
	}

	//4.直线拟合
	//拟合结果为4元素的容器,比如Vec4f-(vx,vy,x0,y0)
	//(vx、vy)是直线的方向向量
	//(x0、y0)是直线上的一个点
	Vec4f fitline;
	fitLine(point_para, fitline, DIST_L2, 0, 0.01, 0.01);

	//4.2 求出直线上的两个点
	double k = fitline[1] / fitline[0];
	Point p1(0, k * (0 - fitline[2]) + fitline[3]);
	Point p2(src.cols - 1, k * ((src.cols - 1) - fitline[2]) + fitline[3]);

	//4.3 显示拟合出的直线方程

	cout << "y-" << fitline[3] << "=" << k << "(x-" << fitline[2] << ")" << endl;
	line(src, p1, p2, Scalar(0, 0, 255), 2);
	imshow("dst", src);
}

int main() {
	Mat src = imread("D:/test/fitline.png");
	if (src.empty()) {
		cout << " input the image error!" << endl;
	}
	imshow("src", src);
	findFitLine(src);


	waitKey(0);
	return 0;

}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

学习:

直线拟合——cv::fitLine()详解
【算法+OpenCV】基于opencv的直线和曲线拟合与绘制(最小二乘法)
opencv学习——最小二乘法拟合直线
OpenCV—直线拟合fitLine
【OpenCV3】直线拟合–FitLine()函数详解

2015-06-09 18:13:08 u012590688 阅读数 1623
           图像处理程序中经常要用到直线检测,常用的直线检测方法是Hough变换。Hough变换是图像处理中从图像中识别几何形状的基本方法之一。Hough变换的基本原理在于
利用点与线的对偶性,将原始图像空间的给定的曲线通过曲线表达形式变为参数空间的一个点。这样就把原始图像中给定曲线的检测问题转化为寻找参数空间中的峰值问

题。也即把检测整体特性转化为检测局部特性。比如直线、椭圆、圆、弧线等。

        简而言之,Hough变换思想为:在原始图像坐标系下的一个点对应了参数坐标系中的一条直线,同样参数坐标系的一条直线对应了原始坐标系下的一个点,然后,原始坐标系下呈现直线的所有点,它们的斜率和截距是相同的,所以它们在参数坐标系下对应于同一个点。这样在将原始坐标系下的各个点投影到参数坐标系下之后,看参数坐标系下有没有聚集点,这样的聚集点就对应了原始坐标系下的直线.

     二值化   边缘检测   hough变换  得到结果











没有更多推荐了,返回首页