直线检测
直线检测可以通过OpenCV的HoughLines和HoughLinesP函数来完成,它们仅有的差别是:第一个函数使用标准的Hough变换,第二个函数使用概率Hough变换,即只通过分析点的子集并估计这些点都属于一条直线的概率,这在计算速度上更快。
函数原型: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:同一方向上两条线段判定为一条线段的最大允许间隔(断裂),超过了设定值,则把两条线段当成一条线段,值越大,允许线段上的断裂越大,越有可能检出潜在的直线段
import cv2 as cv
import numpy as np
def hons_line_demo(img):
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edge = cv.Canny(gray, 50, 150)
lines = cv.HoughLines(edge, 1, np.pi/180, 200)
for line in lines:
rho , theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a + rho
y0 = b + rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
cv.line(img,(x1, y1), (x2, y2),(0, 0, 255), 2)
cv.imshow("line img", img)
def hon_line_demo(img):
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
edge = cv.Canny(gray, 50, 150)
lines = cv.HoughLinesP(edge, 1, np.pi/180, 100 ,minLineLength=50, maxLineGap=10)
for line in lines:
x1, y1, x2, y2 = line[0]
cv.line(img, (x1, y1), (x2, y2), (0, 0 ,255), 2)
cv.imshow("hone_line", img)
src = cv.imread("./../img/8.png") # 读取图像
cv.namedWindow("input img", cv.WINDOW_AUTOSIZE)
cv.imshow("input img", src) # 显示图像
hons_line_demo(src)
hon_line_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
园检测
霍夫圆变换只能检测一些简单情形下的圆形,并且检测不出椭圆
首先对原图进行canny边缘检测,然后用findContours查找轮廓,遍历轮廓上的每一个点,可以得到轮廓上距离最远的两个点,他们的中点可以假定为圆心,这两点间距离可假定为直径,如果轮廓上每个点到这个中点距离在这个半径附近波动,则认为是一个圆(这里最好归一化处理,因为直径大的波动范围,和直径小的波动范围肯定不能用一个阈值)
HoughCircles函数实现了圆形检测,它使用的算法是改进的霍夫变换,该算法把霍夫变换分为两个阶段,从而减小了霍夫空间的维数。第一阶段用于检测圆心,第二阶段从圆心推导出圆半径。
霍夫变换具体步骤:
第一阶段:检测圆心
1.1、对输入图像边缘检测;
1.2、计算图形的梯度,并确定圆周线,其中圆周的梯度就是它的法线;
1.3、在二维霍夫空间内绘出所有图形的梯度直线,坐标点累加和的值越大,则该点上直线相交的次数越多,该点越有可能是圆心;
1.4、在霍夫空间的4邻域内进行非最大值抑制;
1.5、设定一个阈值,霍夫空间内累加和大于该阈值的点就对应于圆心。
第二阶段:检测圆半径
2.1、计算某一个圆心到所有圆周线的距离,这些距离中就有该圆心所对应的圆的半径的值,这些半径值当然是相等的,并且这些圆半径的数量要远远大于其他距离值相等的数量;
2.2、设定两个阈值:最大半径和最小半径。保留距离在这两个半径之间的值,这意味着我们检测的圆不能太大,也不能太小;
2.3、对保留下来的距离进行排序;
2.4、找到距离相同的那些值,并计算相同值的数量;
2.5、设定一个阈值,只有相同值的数量大于该阈值,就认为该值是该圆心对应的圆半径;
2.6、对每一个圆心,完成上面的2.1~2.5步骤,得到所有的圆半径。
def HoughCircles(image, method, dp, minDist, circles=None, param1=None, param2=None, minRadius=None, maxRadius=None):
image为输入图像,要求是灰度图像
method为使用霍夫变换圆检测的算法,Opencv2.4.9只实现了2-1霍夫变换,它的参数是CV_HOUGH_GRADIENT
dp为第一阶段所使用的霍夫空间的分辨率,dp=1时表示霍夫空间与输入图像空间的大小一致,dp=2时霍夫空间是输入图像空间的一半,以此类推
minDist为圆心之间的最小距离,如果检测到的两个圆心之间距离小于该值,则认为它们是同一个圆心
circles为输出圆向量,每个向量包括三个浮点型的元素——圆心横坐标,圆心纵坐标和圆半径
param1为边缘检测时使用Canny算子的高阈值
param2为步骤1.5和步骤2.5中所共有的阈值
minRadius和maxRadius为所检测到的圆半径的最小值和最大值
执行步骤:
1、执行均值偏移滤波,消除噪声
2、转成灰度图
3、canny边缘检测
4、执行霍夫圆检测
5、绘制出检测到的圆
def check_circular_demo(img):
# 均值偏移滤波
dst = cv.pyrMeanShiftFiltering(img, 10, 100)
# 转成灰度图
gray = cv.cvtColor(dst, cv.COLOR_BGR2GRAY)
# 执行霍夫圆检测
circular = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, 20, param1=50, param2=30, minRadius=0, maxRadius=0)
'''
HoughCircles(image, method, dp, minDist, circles=None, param1=None, param2=None, minRadius=None, maxRadius=None)
image: 输入图像
method: 检测方法
minDist: 检测到圆心之间的最小距离 img.rows/8
circles: 返回结果 (x,y,r)
param1: canny边缘函数的高阈值
param2: 圆心检测阈值
minRadius: 能检测到的最小圆半径
maxRadius: 能检测到的最大圆半径
'''
circular = np.uint16(np.around(circular))
for i in circular[0, :]:
cv.circle(img, (i[0], i[1]), i[2], (0, 0, 255), 2)
cv.circle(img, (i[0], i[1]), 2, (255, 0, 0), 2)
cv.imshow("circular", img)