1 实验目的
了解直线检测的原理和绘制直线.
2 实验设备
安装了python和pychrm的电脑一台。
3 实验内容
图片的导入、灰度化处理、直线检测,绘制找出的直线、显示图片。
4实验原理
霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进算法。主要用来从图像中分离出具有某种相同特征的几何形状(如,直线,圆等)。最基本的霍夫变换是从黑白图像中检测直线(线段)。
标准霍夫变换本质上是把图像映射到它的参数空间上,它需要计算所有的M个边缘点,这样它的运算量和所需内存空间都会很大。如果在输入图像中只是处理m(m<M)个边缘点,则这m个边缘点的选取是具有一定概率性的,因此该方法被称为概率霍夫变换(Probabilistic Hough Transform)。该方法还有一个重要的特点就是能够检测出线端,即能够检测出图像中直线的两个端点,确切地定位图像中的直线。
霍夫变换在检测各种形状的的技术中非常流行,如果你要检测的形状可以用数学表达式写出,你就可以是使用霍夫变换检测它。及时要检测的形状存在一点破坏或者扭曲也可以使用。我们下面就看看如何使用霍夫变换检测直线一条直线可以用数学表达式 y = mx + c 或者 ρ = x cos θ + y sin θ 表示。ρ 是从原点到直线的垂直距离,θ 是直线的垂线与横轴顺时针方向的夹角(如果你使用的坐标系不同方向也可能不同,我是按 OpenCV 使用的坐标系描述的)。如下图所示:
线性方程示意图
所以如果一条线在原点下方经过,ρ 的值就应该大于 0,角度小于 180。但是如果从原点上方经过的话,角度不是大于 180,也是小于 180,但 ρ 的值小于 0。垂直的线角度为 0 度,水平线的角度为 90 度。
让我们来看看霍夫变换是如何工作的。每一条直线都可以用 (ρ, θ) 表示。所以首先创建一个 2D 数组(累加器),初始化累加器,所有的值都为 0。行表示 ρ,列表示 θ。这个数组的大小决定了最后结果的准确性。如果你希望角度精确到 1 度,你就需要 180 列。对于 ρ,最大值为图片对角线的距离。所以如果精确度要达到一个像素的级别,行数就应该与图像对角线的距离相等。
想象一下我们有一个大小为 100x100 的直线位于图像的中央。取直线上的第一个点,我们知道此处的(x,y)值。把 x 和 y 带入上边的方程组,然后遍历 θ 的取值:0,1,2,3,. . .,180。分别求出与其对应的 ρ 的值,这样们就得到一系列(ρ, θ)的数值对,如果这个数值对在累加器中也存在相应的位置,就在这个位置上加 1。所以现在累加器中的(50,90)=1。(一个点可能存在与多条直线中,所以对于直线上的每一个点可能是累加器中的多个值同时加 1)。
现在取直线上的第二个点。重复上边的过程。更新累加器中的值。现在累加器中(50,90)的值为 2。你每次做的就是更新累加器中的值。对直线上的每个点都执行上边的操作,每次操作完成之后,累加器中的值就加 1,但其他地方有时会加 1, 有时不会。按照这种方式下去,到最后累加器中(50,90)的值肯定是最大的。如果你搜索累加器中的最大值,并找到其位置(50,90),这就说明图像中有一条直线,这条直线到原点的距离为 50,它的垂线与横轴的夹角为 90 度。
直线检测可以通过OpenCV的HoughLines和HoughLinesP函数来完成,它们仅有的差别是:第一个函数使用标准的Hough变换,第二个函数使用概率Hough变换,即只通过分析点的子集并估计这些点都属于一条直线的概率,这在计算速度上更快。
HoughLinesP函数就是利用概率霍夫变换来检测直线的。它的一般步骤为:
1、随机抽取图像中的一个特征点,即边缘点,如果该点已经被标定为是某一条直线上的点,则继续在剩下的边缘点中随机抽取一个边缘点,直到所有边缘点都抽取完了为止;
2、对该点进行霍夫变换,并进行累加和计算;
3、选取在霍夫空间内值最大的点,如果该点大于阈值的,则进行步骤4,否则回到步骤1;
4、根据霍夫变换得到的最大值,从该点出发,沿着直线的方向位移,从而找到直线的两个端点;
5、计算直线的长度,如果大于某个阈值,则被认为是好的直线输出,回到步骤1。
函数原型:
HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None)
参数:
image:二值图像,推荐使用canny边缘检测的结果图像;
rho:线段以像素为单位的距离精度,double类型的,推荐用1.0;
theta:线段以弧度为单位的角度精度,推荐用numpy.pi/180;
threshold:累加平面的阈值参数,int类型,超过设定阈值才被检测出线段,值越大,基本上意味着检出的线段越长,检出的线段个数越少。根据情况推荐先用100试试;
lines:这个参数的意义未知,发现不同的lines对结果没影响,但是不要忽略了它的存在;
minLineLength:线段以像素为单位的最小长度,根据应用场景设置;
maxLineGap:同一方向上两条线段判定为一条线段的最大允许间隔(断裂),超过了设定值,则把两条线段当成一条线段,值越大,允许线段上的断裂越大,越有可能检出潜在的直线段。
函数返回的是储存着检测到的直线的参数对的容器,也就是线段两个端点的坐标,所以我们绘制这条直线,绘制函数还是用cv2.drawContours函数绘制。
5 实验步骤
打开pycharm。
我们右击相应的文件目录,选择new--->点击Python File,然后输入新建的文件名,点击确定,相应的.py文件就建好了,可以进行编写代码了。
import cv2
import numpy as np
读取图片并转换为灰度图像。
img = cv2.imread("..\images\ color1_small.jpg ")
将图像转为灰度图像。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
我们检测直线之前最好先使用canny边缘检测的结果图像。
edges = cv2.Canny(gray, 50, 120)
调用HoughLinesP函数检测直线。
参数:
edgest:边缘检测的输出图像,它应该是个灰度图作为我们的输入图像。
1:参数极径 r 以像素值为单位的分辨率. 我们使用 1 像素。
np.pi/180:参数极角\theta以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)。
20:要”检测” 一条直线所需最少的的曲线交点。
minLineLength:能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃。
maxLineGap:线段上最近两点之间的阈值,也就是能被认为在一条直线上的亮点的最大距离。
minLineLength = 20
maxLineGap = 5
lines = cv2.HoughLinesP(edges, 1.0, np.pi / 180, 20, minLineLength, maxLineGap)
通过画出检测到的直线来显示结果
for line in lines:
for x1, y1, x2, y2 in line:
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 1)
显示绘制轮廓的图像
cv2.imshow("lines", img)
cv2.imshow("edges", edges)
cv2.waitKey()
下面是直线检测和canny边缘检测的效果图。
运行效果图