原理介绍:
1、对于直角坐标系中的任意一点A(x0,y0),经过点A的直线满足Y0=k*X0+b.(k是斜率,b是截距)
2、那么在X-Y平面过点A(x0,y0)的直线簇可以用Y0=k*X0+b表示,但对于垂直于X轴的直线斜率是无穷大的则无法表示。因此将直角坐标系转换到极坐标系就能解决该特殊情况。
3、在极坐标系中表示直线的方程为ρ=xCosθ+ySinθ(ρ为原点到直线的距离),如图所示:
变换图示
霍夫直线检测的两种方法
1.获取灰度图像
2.canny边缘检测
3.获取霍夫直线信息
4.算出直线位置,画出每条直线
直线检测代码实现方法1
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
参数:
image:边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图)
lines:储存着检测到的直线的参数对 的容器,存储的是rho,theta
rho:参数极径 以像素值为单位的分辨率. 我们使用 1 像素.
theta:参数极角 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
theta:要”检测” 一条直线所需最少的的曲线交点
srn and stn: 参数默认为0.
cv2.HoughLines函数输出的是[float, float]形式的ndarray,其中每个值表示检测到的线(ρ , θ)中浮点点值的参数。
import cv2 as cv
import numpy as np
def line_detection(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 50, 150, apertureSize=3)#apertureSize,Canny边缘检测梯度那一步,窗口大小是3 apertureSize是sobel算子大小,只能为1,3,5,7
lines = cv.HoughLines(edges, 1, np.pi/180, 200) #函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线
for line in lines:
print(type(lines))
rho, theta = line[0] #获取极值ρ长度和θ角度
a = np.cos(theta) #获取角度cos值
b = np.sin(theta)#获取角度sin值
x0 = a * rho #获取x轴值
y0 = b * rho #获取y轴值 x0和y0是直线的中点
x1 = int(x0 + 1000 * (-b)) #获取这条直线最大值点x1
y1 = int(y0 + 1000 * (a)) #获取这条直线最大值点y1
x2 = int(x0 - 1000 * (-b)) #获取这条直线最小值点x2
y2 = int(y0 - 1000 * (a)) #获取这条直线最小值点y2 其中*1000是内部规则
cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) #开始划线
cv.imshow("image_lines", image)
src = cv.imread("C:/Users/lenovo/Desktop/opencv/daima/banknum/template-matching-ocr/images/sudoku.png") #读取图片位置
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
line_detection(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行截图:
但这样会在一些情况下导致虚假检测,如像素偶然对齐或多条直线穿过同样的对齐像素造成的多重检测。
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold,double minLineLength=0, double maxLineGap=0 )
参数:
image: 边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图) *
lines: 储存着检测到的直线的参数对 的容器,也就是线段两个端点的坐标
rho : 参数极径 以像素值为单位的分辨率. 我们使用 1 像素.
theta: 参数极角 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
threshold: 要”检测” 一条直线所需最少的的曲线交点
minLinLength: 能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃.线段的最小长度
maxLineGap:线段上最近两点之间的阈值
代码如下:
import cv2 as cv
import numpy as np
def line_detect_possible_demo(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray, 50, 150, apertureSize=3) # apertureSize,Canny边缘检测梯度那一步,窗口大小是3
lines = cv.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=50, maxLineGap=10) #函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线
#minLineLength-线的最短长度,比这个线短的都会被忽略
#maxLineGap-两条线之间的最大间隔,如果小于此值,这两条线就会被看成一条线。
for line in lines:
print(type(line))
x1, y1, x2, y2 = line[0]
cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv.imshow("line_detect_possible_demo", image)
src = cv.imread("C:/Users/lenovo/Desktop/opencv/daima/banknum/template-matching-ocr/images/sudoku.png") #读取图片位置
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
line_detect_possible_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行截图:
def HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None):
第一个参数是需要处理的原图像,该图像必须为cannay边缘检测后的图像;
第二和第三参数:步长为1的半径和步长为π/180的角来搜索所有可能的直线第四个参数是阈值,概念同霍夫变换第五个参数:minLineLength-线的最短长度,比这个线短的都会被忽略。
第六个参数:maxLineGap-两条线之间的最大间隔,如果小于此值,这两条线就会被看成一条线。