python+opencv霍夫直线、圆检测
一、霍夫空间
在笛卡尔坐标系中直线
y
=
k
x
+
q
y=kx+q
y=kx+q可以由A(
x
1
x_1
x1,
y
1
y_1
y1),B(
x
2
x_2
x2,
y
2
y_2
y2)两点确定
在该坐标系中,
x
x
x,
y
y
y分别为自变量和因变量,若此时我们将
y
=
k
x
+
q
y=kx+q
y=kx+q写成关于
k
k
k,
q
q
q的函数表达式(霍夫空间):
{
q
=
−
k
x
1
+
y
1
q
=
−
k
x
2
+
y
2
\begin{cases} q =-kx_1+y_1 \\[2ex] q=-kx_2+y_2 \end{cases}
⎩⎨⎧q=−kx1+y1q=−kx2+y2
对应图像变换如下:
变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点。
反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):
A、B两个点对应的霍夫空间图像:
多个点对应图像:
霍夫变换的后处理的基本方式:选择由尽可能多直线汇成的点。
霍夫空间:选择由三条交汇直线确定的点,对应的笛卡尔坐标系的直线
霍夫变换由此已经很清楚了,但现在我们考虑直角坐标系中直线平行于坐标轴的情况:
该直线表达式中
k
k
k可以趋近于无穷大,这种情况下我们考虑将直角坐标系转换为极坐标系:
对于直角坐标系与极坐标系的转换:
{
x
=
ρ
c
o
s
θ
y
=
ρ
s
i
n
θ
\begin{cases} x=\rho cos \theta \\[2ex] y=\rho sin \theta \end{cases}
⎩⎨⎧x=ρcosθy=ρsinθ
这种情况下,我们霍夫空间的参数变为
θ
\theta
θ,
ρ
\rho
ρ,图像对比变换如下:
二、霍夫直线检测
1、直线检测函数
在opencv中给出了两个霍夫直线检测的函数:
标准霍夫变换(多尺度霍夫变换):
HoughLines( InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double srn = 0, double stn = 0,
double min_theta = 0, double max_theta = CV_PI );
其中各参数解释如下:
InputArray image:输入图像,必须是8位单通道图像。
OutputArray lines:检测到的线条参数集合。
double rho:以像素为单位的距离步长。
double theta:以弧度为单位的角度步长。
int threshold:累加计数值的阈值参数,当参数空间某个交点的累加计数的值超过该阈值,则认为该交点对应了图像空间的一条直线。
double srn:默认值为0,用于在多尺度霍夫变换中作为参数rho的除数,rho=rho/srn。
double stn:默认值为0,用于在多尺度霍夫变换中作为参数theta的除数,theta=theta/stn。
如果srn和stn同时为0,就表示HoughLines函数执行标准霍夫变换,否则就是执行多尺度霍夫变换。
渐进式霍夫变换:
HoughLinesP( InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double minLineLength = 0, double maxLineGap = 0 )
其中各参数解释如下:
InputArray image:输入图像,必须是8位单通道图像。
OutputArray lines:检测到的线条参数集合。
double rho:直线搜索时的距离步长,以像素为单位。
double theta:直线搜索时的角度步长,以弧度为单位。
int threshold:累加计数值的阈值参数,当参数空间某个交点的累加计数的值超过该阈值,则认为该交点对应了图像空间的一条直线。
double minLineLength:默认值为0,表示最小线段长度阈值(像素)。
double maxLineGap:线段上最近两点之间的阈值.默认值为0,表示直线断裂的最大间隔距离阈值。即如果有两条线段是在一条直线上,但它们之间有间隙,那么如果这个间隔距离小于该值,则被认为是一条线段,否则认为是两条线段。
2、直线检测实例
此次直线检测的基本步骤如下:
1、图像灰度化处理
#将图像转换为灰度图像
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
#cv.imshow("gray",gray)
2、对灰度处理的图片进行直方图均衡化处理
#应用直方图均衡化
dst = cv.equalizeHist(gray)
#cv.imshow("dst",dst)
3、对均衡处理的图片进行高斯处理
#高斯滤波降噪
gaussian = cv.GaussianBlur(dst, (11,11), 0)
#cv.imshow("gaussian",gaussian)
4、利用canny算法进行边缘检测
#利用Canny进行边缘检测
edges = cv.Canny(gaussian, 80,180, apertureSize=3)
#cv.imshow("edge",edges)
完整程序如下:
#1霍夫直线检测
import cv2 as cv
import numpy as np
src = cv.imread(r'house.jpg')
#cv.imshow("origin",src)
#将图像转换为灰度图像
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
#cv.imshow("gray",gray)
#应用直方图均衡化
dst = cv.equalizeHist(gray)
#cv.imshow("dst",dst)
#高斯滤波降噪
gaussian = cv.GaussianBlur(dst, (11,11), 0)
#cv.imshow("gaussian",gaussian)
#利用Canny进行边缘检测
edges = cv.Canny(gaussian, 80,180, apertureSize=3)
#cv.imshow("edge",edges)
#自动检测可能的直线,返回的是一条条线段
#第二个参数为半径的步长,第三个参数为每次偏转的角度
lines = cv.HoughLinesP(edges, 1, np.pi/180, 80, minLineLength=37, maxLineGap=6)
print(type(lines))
for line in lines:
x1, y1, x2, y2 = line[0]
cv.line(src, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv.imshow("line",src)
cv.waitKey(0)
cv.destroyAllWindows()
在该程序代码中,由于采用的处理图片不同,可以通过调节高斯核大小、canny检测的阈值数据、HoughLinesP函数中的最小显示线段长度等参数 改变图片显示效果
我的程序运行结果如下:
由图片显示结果可知,这些参数定义对于我处理的图片是比较适合的。
三、霍夫圆检测
1、霍夫圆检测函数
HoughCircles(InputArray image,//输入图像,8位的单通道灰度图
OutputArray circles,//检测圆的输出矢量,每个圆由包含了3元素浮点型的向量(x,y,r)
int method,//检测方法,看 cv::HoughModes里面
double dp,//如果dp=2,累加器是输入图像一半的高度和宽度
double minDist,//圆心之间的最小距离,如果太小多个相邻圆可能被错误的检测成一个重合圆。如果太大,某些圆可能检测不出来。
double param1 = 100,//Canny算子的高阈值,低阈值为它的一半
double param2 = 100,//最小投票数,越小,越多假的圆被检测出来。
int minRadius = 0, //最小圆半径
int maxRadius = 0);//最大圆半径
2、圆检测实例
此次霍夫圆检测步骤如下:
1、图片灰度图转换
#将图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
2、高斯滤波处理
#高斯滤波降噪
gaussian = cv2.GaussianBlur(gray, (7, 7),0)
3、canny边缘检测
#利用Canny进行边缘检测
edges = cv2.Canny(gaussian, 80,180, apertureSize=3)
#cv2.imshow("edge",edges)
4、霍夫圆检测
#自动检测圆
circles1 = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 105, param1=180, param2=30,minRadius=50, maxRadius=95)
print(np.shape(circles1))
程序完整代码如下:
import cv2
import numpy as np
img = cv2.imread('coins.png')
#cv2.imshow('img_yuantu', img)
#将图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#高斯滤波降噪
gaussian = cv2.GaussianBlur(gray, (7, 7),0)
#利用Canny进行边缘检测
edges = cv2.Canny(gaussian, 80,180, apertureSize=3)
#cv2.imshow("edge",edges)
#自动检测圆
circles1 = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 105, param1=180, param2=30,minRadius=50, maxRadius=95)
print(np.shape(circles1))
circles = circles1[0, :, :]
circles = np.uint16(np.around(circles))
for i in circles[:]:
cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 3)
#cv2.circle(img, (i[0], i[1]), 2, (255, 0, 255), 10)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
该程序执行结果如下:
跟直线检测一样,不同的图片对于相关参数要求不同,需要通过不断的参数调整得到最好的图片处理结果,由图片显示可知,我代码中的相关参数对于我的图片适配度也是很高的,但是就同一张图片而言也可能具有其他不同参数配置可以达到相同效果的哦!
相关霍夫空间与霍夫变换的讲解参考博客:https://www.cnblogs.com/php-rearch/p/6760683.html