Hough变换
- 一、Hough线变换
- 1.1 标准、多尺度hough变换cv2.HoughLine()
- 1.2 渐进概率hough变换cv2.HoughLineP()
- 二、Hough圆变换cv2.HoughCircles()
hough变换是一种用于检测线、圆或者图像中其他简单形状的方法。
一、Hough线变换
hough线变换的基本理论是:二进制图像中的任何点都可能属于某些可能的线。如果将每条线参数化,如斜率为a,截距为b,原始图像中的点就可以转换为对应于通过该点的所有线在该平面(a,b)中的点的轨迹(可能是一部分轨迹)。如果将图像中每个非0像素都转换成输出图像中这样的一系列点,并将这些贡献相加,那么原图((x,y)平面)中的线将显示为输出图像((a,b)平面)中的局部最大值,该平面称为累加器平面。
斜截式方程并不是代表所有穿过某个点的线的最佳方式,最好的方式是用极坐标中的点(ρ,θ)表示线
通过极坐标转换:直角坐标中的线对应于极坐标中的一个点。直角坐标中的一个点对应于极坐标的一条线.
opencv支持三种hough线变换:标准hough变换(SHT)、多尺度hough变换(MHT)和渐进概率hough变换(PPHT)
1.1 标准、多尺度hough变换cv2.HoughLine()
lines= HoughLines(image, rho, theta, threshold, srn=None, stn=None, min_theta=None, max_theta=None)
参数 | 含义 |
image | 8位图像,单通道二进制图像 |
lines | 结果:一个N×1的双通道浮点型数组(列数N是返回的直线数目),两个通道分别包含每条线的rho(ρ)和theta(θ)值 |
rho | 粗略的累加器距离分辨率(以像素为单位)。 |
theta | 粗略的累加器角度分辨率(以弧度为单位)。 |
threshold | 累加平面可以看作一个由θ弧度大小为ρ像素的单元组层的二维直方图。threshold是累加平面中算法用于判断线条属于一条直线的阈值。 |
srn=None | 对于多尺度hough变换,它是距离分辨率rho的除数。粗略的累加器距离分辨率为rho,准确的累加器分辨率为rho / srn。 如果srn = 0和stn = 0,则是标准Hough变换。 否则这两个参数应为正。 |
stn=None | 对于多尺度hough变换,它是角度分辨率θ的除数。 |
min_theta=None | 对于标准和多尺度hough变换,使用最小角度检查线条。 必须介于0和max_theta之间。 |
max_theta=None | 对于标准和多尺度hough变换,请使用最大角度来检查线条。 必须介于min_theta和CV_PI之间。 |
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
'''边缘检测'''
def canny_demo(image):
# 1.高斯模糊:最后两个参数给定一个就可以自动求取另一个,所以只给其中一个赋值就可以
blur=cv.GaussianBlur(image,(3,3),0)
#2.灰度处理
gray=cv.cvtColor(blur,cv.COLOR_BGR2GRAY)
#3.求取梯度,也可以不用梯度,直接将灰度图传入cv.canny
grad_x=cv.Sobel(gray,cv.CV_16SC1,1,0)
grad_y = cv.Sobel(gray, cv.CV_16SC1, 0, 1)
#4.求边缘
edge_output=cv.Canny(grad_x,grad_y,50,150)#参数:x梯度,y梯度 ,低阈值,高阈值
cv.imshow('canny-demo',edge_output)
return edge_output
'''直线检测'''
def huf_line_detect():
img = cv.imread(r'D:\Project\Opencv\Learning01\line.jpg')
edge_output=canny_demo(img)
lines=cv.HoughLines(edge_output,1,np.pi/180,150)#参数:边缘图像,半径,角度,高值
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))
# 绘制直线:参数图像,点1,点2,颜色,直线宽度
cv.line(img,(x1,y1),(x2,y2),(0,0,255),2)
cv.imshow('img',img)
img = cv.imread(r'D:\Project\Opencv\Learning01\line.jpg')
cv.imshow('origin',img)
huf_line_detect()
cv.waitKey(0)
1.2 渐进概率hough变换cv2.HoughLineP()
lines= HoughLinesP(image, rho, theta, threshold, minLineLength=None, maxLineGap=None)
参数 | 含义 |
image | 8位图像,单通道二进制图像,既二值图像 |
lines | 结果:一个四通道,四通道分别找出线段两个端点的坐标(x0,y0)和(x1,y1) |
rho | 粗略的累加器距离分辨率(以像素为单位)。 |
theta | 粗略的累加器角度分辨率(以弧度为单位)。 |
threshold | 累加平面可以看作一个由θ弧度大小为ρ像素的单元组层的二维直方图。threshold是累加平面中算法用于判断线条属于一条直线的阈值。 |
minLineLength=None | 返回线段的最小长度 |
maxLineGap=None | 共线线段的最小间隔,防止把他们连接成一条。 |
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
'''边缘检测'''
def canny_demo(image):
# 1.高斯模糊:最后两个参数给定一个就可以自动求取另一个,所以只给其中一个赋值就可以
blur=cv.GaussianBlur(image,(3,3),0)
#2.灰度处理
gray=cv.cvtColor(blur,cv.COLOR_BGR2GRAY)
#3.求取梯度,也可以不用梯度,直接将灰度图传入cv.canny
grad_x=cv.Sobel(gray,cv.CV_16SC1,1,0)
grad_y = cv.Sobel(gray, cv.CV_16SC1, 0, 1)
#4.求边缘
edge_output=cv.Canny(grad_x,grad_y,50,150)#参数:x梯度,y梯度 ,低阈值,高阈值
cv.imshow('canny-demo',edge_output)
return edge_output
def line_detect_huf():
img = cv.imread(r'D:\Project\Opencv\Learning01\line.jpg')
edge_output=canny_demo(img)
lines=cv.HoughLinesP(edge_output,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('img', img)
img = cv.imread(r'D:\Project\Opencv\Learning01\line.jpg')
cv.imshow('origin',img)
line_detect_huf()
cv.waitKey(0)
二、Hough圆变换cv2.HoughCircles()
opencv通过hough梯度法来进行圆检测。
hough梯度法:
(1)对图像进行边缘检测,这里使用canny边缘检测
(2)对每个轮廓图像的非零点,计算局部梯度(通过sobel计算x和y方向的一阶梯度)
(3)通过梯度,沿着斜率表示的线在累加器内从一个最小值到一个最大值遍历每个点,同时记录轮廓图像中每个非0像素所在的位置。候选圆心就是从这些累加器中分离出来,这些点都高于一个阈值且同时大于其所有直接相邻的点。这些点根据其累加器值的降序排列,使得最优可能是圆心的点排在前面。
(4)对每个圆心,考虑所有非零像素点,将这些像素根据离圆心的距离排序。
(5)从最小距离到最大半径中选择一个最好的值作为圆的半径。如果有足够数量的点组合成一个圆,并且其圆心与之前选中圆心的聚集足够大,就保留这个圆心。
circles= HoughCircles(image, method, dp, minDist, param1=None, param2=None, minRadius=None, maxRadius=None)
参数 | 含义 |
image | 8位图像,单通道灰度图像。hough直线检测需要二值图像 |
circles | 结果:找到的圆的输出向量。 每个向量都编码为3元素浮点向量(x,y,radius)原点坐标和半径 |
method | 检测方法,目前只支持cv2.HOUGH_GRADIENT |
dp | 累加器分辨率与图像分辨率的反比。 例如,如果dp = 1,则累加器具有与输入图像相同的分辨率。 如果dp = 2,则累加器的宽度和高度是其一半。 |
minDist | 检测到的圆心之间的最小距离。 如果参数太小,则除了真实的圆圈外,还可能会错误地检测到多个临接圆圈。 如果太大,可能会错过一些圆圈。 |
param1=None | 边阈值 |
param2=None | 累加器阈值 |
minRadius=None | 圆的最小半径 |
maxRadius=None | 圆的最大半径 |
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
'''圆检测'''
def circle_detect():
img = cv.imread(r'D:\Project\Opencv\Learning01\monney.jpg')
#边缘保留滤波/高斯滤波
dst=cv.pyrMeanShiftFiltering(img,10,100)
gray=cv.cvtColor(dst,cv.COLOR_BGR2GRAY)
#霍夫圆检测,跟直线检测一样可以不用梯度图
circles=cv.HoughCircles(gray,cv.HOUGH_GRADIENT,1,35,param1=50,param2=30,minRadius=10,maxRadius=0)
circles=np.uint16(np.around(circles))
for circle in circles[0,:]:
cv.circle(img,(circle[0],circle[1]),circle[2],(0,0,255),2)
cv.circle(img, (circle[0], circle[1]), 2, (255, 0, 0), 2)
cv.imshow('img',img)
img = cv.imread(r'D:\Project\Opencv\Learning01\monney.jpg')
cv.imshow('origin',img)
circle_detect()
cv.waitKey(0)