图像空间中的直线可以用两个变量表示。例如:
x = ρ cos θ , y = ρ cos θ x=\rho\cos\theta , y=\rho\cos\theta x=ρcosθ,y=ρcosθ
ρ 2 = x 2 + y 2 , tan θ = y / x ( x 不 等 于 0 ) \rho^2=x^2+y^2 , \tan\theta=y/x (x不等于0) ρ2=x2+y2,tanθ=y/x(x不等于0)
霍夫变换基本原理是:二进制图像中的任何点都可能属于某些可能的线。如果为我们将每一条线参数化,比如斜率为 a a a ,截距为 b b b ,原始图像中的点就可以转换为对应于通过该点的所有线在该平面 ( a , b ) (a,b) (a,b) 中的点的轨迹,也可能只是一部分根轨迹。如果我们将原图中的每个非0 像素 都转换成输出图像中这样的一系列点,并将所有这些贡献相加,那么原图 ( ( x , y ) 平 面 ) ((x,y)平面) ((x,y)平面) 中的线将显示为输出图像 ( ( a , b ) 平 面 ) ((a,b)平面) ((a,b)平面) 中的局部最大值。由于我们对每个点的贡献进行求和 ,所以 ( ( a , b ) 平 面 ) ((a,b)平面) ((a,b)平面) 通常称为" 累加器平面"。
但是可能会发现,斜截式方程并不是代表所有穿过某个点的线的最佳方式( 因为图像中线的密度和斜率有很大的不同,还有一个相关的事实是,斜率的区间可能从 − ∞ -\infty −∞ 到 + ∞ +\infty +∞ ,正是因为如此,图像变换时数值计算实际用到的参数有所不同,最好的参数形式是用极坐标中的点( ρ \rho ρ, θ \theta θ)表示线,每条直线经过这个点并且垂直于它与原点相连的线) 如图:
图像平面 (A) 中有一个点可以表示很多直线,每条直线都可以有 (B) 中的 ρ \rho ρ 和 θ \theta θ 参数化,在 ( ρ \rho ρ, θ \theta θ) 平面中,这些线组合在一起形成特殊的形状 (C) 的曲线
对于霍夫变换,我们将在极坐标系统中表示直线。因此,直线方程可以写成:
y = ( − cos θ sin θ ) x + ( r sin θ ) y=(-\frac{\cos\theta}{\sin\theta})x +(\frac{r}{\sin\theta}) y=(−sinθcosθ)x+(sinθr)
1:Arranging the terms(极经): r = x c o s θ + y s i n θ r=xcosθ+ysinθ r=xcosθ+ysinθ
一般对于每个点(x0,y0),我们可以定义通过该点的直线族为:
r θ = x 0 ∗ cos θ + y 0 ∗ sin θ r\theta=x_0 * \cos\theta +y_0 * \sin\theta rθ=x0∗cosθ+y0∗sinθ
θ \theta θ 的取值范围在 0° ~ 180° 或 0° ~ 360° 之间, 每次取 1°,(x,y)坐标不变 获取一次 r (极坐标的极经)值,获取一定范围内的点绘制成曲线。即:
每一对( r θ rθ rθ, θ θ θ)代表,经过每一行( x 0 x_0 x0, y 0 y_0 y0)。
2:便利所有像素点,绘制曲线,绘制无数条曲线,这些曲线相交与一点,这表明,这些像素点都属于同一条直线。
对于任意一条直线上的点来说,变换到极坐标中, 从 [0 ~ 360] 空间 可以得到 r 的大小,属于同一条直线上的点在极坐标空间 (r,θ) 必然在一个点上有最强的信号出现,据此反算到平面坐标中就可以得到直线上各个点的坐标。从而得到直线。
标准的霍夫变换
Cv2.HoughLines():使用标准霍夫变换查找二进制图像中的行。从平面坐标转换到极坐标空间,最终返回输出 (r,θ)表示极坐标空间。
返回值: LineSegmentPolar[]
结果需要自己反变换到平面空间,使用难度较大!
参数 | 描述 |
---|---|
InputArray image | 输入图像,8位、单通道、二进制源图像。 |
double rho | 累加器的距离分辨率(以像素为单位),(生成极坐标时的像素扫描的步长) |
double theta | 累加器的角度分辨率(以弧度为单位) (生成极坐标的角度步长,一般为 1°) |
int threshold | 累加器阈值参数,只有获取足够交点的极坐标才能看作直线 |
double srn = 0 | 对于多尺度霍夫变换,它是距离分辨率的因子。[默认值为0] 如果不是设置0 表示经典霍夫变换 |
double stn = 0 | 对于多尺度霍夫变换,它是距离分辨率的因子。[默认值为0] 如果不是设置0 表示经典霍夫变换 |
霍夫变换直线概率
**Cv2.HoughLinesP:**使用概率霍夫变换查找二进制图像中的线段。最终输出的是直线的两个点坐标
返回值类型:LineSegmentPoint[]
参数 | 描述 |
---|---|
InputArray image | 输入图像,8位、单通道、二进制源图像。 |
double rho | 累加器的距离分辨率(以像素为单位),(生成极坐标时的像素扫描的步长) |
double theta | 累加器的角度分辨率(以弧度为单位) (生成极坐标的角度步长,一般为 1°) |
int threshold | 阈值参数,只有获取足够交点的极坐标才能看作直线 |
double minLineLength = 0 | 最小线长度。比这短的线段将被拒绝。[默认值为0] |
double maxLineGap = 0 | 同一条线上的点之间连接它们的最大允许间隙。[默认值为0] |
///
/// 霍夫变换-直线
///
///
private static void HoughtLine(string imagePath)
{
using (Mat srcLine = new Mat(imagePath, ImreadModes.AnyColor | ImreadModes.AnyDepth))
using (Mat dst = new Mat(srcLine.Size(), MatType.CV_8UC3, Scalar.Blue))
{
// 1:边缘检测
Mat canyy = new Mat(srcLine.Size(), srcLine.Type());
Cv2.Canny(srcLine, canyy, 60, 200, 3, false);
/*
* HoughLinesP:使用概率霍夫变换查找二进制图像中的线段。
* 参数:
* 1; image: 输入图像 (只能输入单通道图像)
* 2; rho: 累加器的距离分辨率(以像素为单位) 生成极坐标时候的像素扫描步长
* 3; theta: 累加器的角度分辨率(以弧度为单位)生成极坐标时候的角度步长,一般取值CV_PI/180 ==1度
* 4; threshold: 累加器阈值参数。只有那些足够的行才会返回 投票(>阈值);设置认为几个像素连载一起 才能被看做是直线。
* 5; minLineLength: 最小线长度,设置最小线段是有几个像素组成。
* 6;maxLineGap: 同一条线上的点之间连接它们的最大允许间隙。(默认情况下是0):设置你认为像素之间 间隔多少个间隙也能认为是直线
* 返回结果:
* 输出线。每条线由一个4元向量(x1, y1, x2,y2)
*/
LineSegmentPoint[] linePiont = Cv2.HoughLinesP(canyy, 1, 1, 1, 1, 10);//只能输入单通道图像
Scalar color = new Scalar(0, 255, 255);
for (int i = 0; i < linePiont.Count(); i++)
{
Point p1 = linePiont[i].P1;
Point p2 = linePiont[i].P2;
Cv2.Line(dst, p1, p2, color, 4, LineTypes.Link8);
}
using (new Window("DST", WindowMode.AutoSize, dst))
using (new Window("CANYY", WindowMode.AutoSize, canyy))
using (new Window("SRC", WindowMode.AutoSize, srcLine))
{
Cv2.WaitKey(0);
}
}
}