霍夫变换原理
霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,它通过一种投票算法检测具有特定形状的物体。Hough变换是图像处理中从图像中识别几何形状的基本方法之一。Hough变换的基本原理在于利用点与线的对偶性,将原始图像空间的给定的曲线通过曲线表达形式变为参数空间的一个点。这样就把原始图像中给定曲线的检测问题转化为寻找参数空间中的峰值问题。也即把检测整体特性转化为检测局部特性。比如直线、椭圆、圆、弧线等。
说起直线,我们会想到笛卡尔坐标系(即x-y坐标系)下的直线方程,细分之则有点斜式、截距式等, y = k x + b y=kx+b y=kx+b 是我们最熟悉的一种,但直线垂直于x轴时斜率 k k k不存在,这给我们带来许多不便之处。
这时笛卡尔坐标系就能转换为极坐标系:
p
=
x
c
o
s
θ
+
y
s
i
n
θ
p=xcos\theta + y sin\theta
p=xcosθ+ysinθ,变形可得:
如上图,假定在一个88的平面像素中有一条直线,并且从左上角(1,8)像素点开始分别计算θ为0°、45°、90°、135°、180°时的ρ,图中可以看出ρ分别为1、(9√2)/2、8、(7√2)/2、-1,并给这5个值分别记一票,同理计算像素点(3,6)点θ为0°、45°、90°、135°、180°时的ρ,再给计算出来的5个ρ值分别记一票,此时就会发现ρ = (9√2)/2的这个值已经记了两票了,以此类推,遍历完整个88的像素空间的时候ρ = (9√2)/2就记了5票, 别的ρ值的票数均小于5票,所以得到该直线在这个88的像素坐标中的极坐标方程为 (9√2)/2=xCos45°+y*Sin45°,到此该直线方程就求出来了。(PS:但实际中θ的取值不会跨度这么大,一般是PI/180)。
上述图文说明引用自Opencv学习笔记-----霍夫变换直线检测及原理理解
关于霍夫变换还有其它资料,可以看最后的参考与推荐。这里我就不再介绍了,我们直接来看opencv的代码。
cv2.HoughCircles
import cv2
import numpy as np
planets = cv2.imread('640.jpg')
gray_img = cv2.cvtColor(planets,cv2.COLOR_BGR2GRAY)
img = cv2.medianBlur(gray_img,5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,120,param1=100,param2=30,minRadius=0,maxRadius=0)
# circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, cimg.shape[0]/64, param1=p1, param2=p2, minRadius=25, maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
#画出外圆
cv2.circle(planets,(i[0],i[1]),i[2],(0,255,0),6)
# 画出圆心
cv2.circle(planets,(i[0],i[1]),2,(0,0,255),3)
cv2.imshow("HoughCirlces",planets)
cv2.imwrite("HoughCirlces.jpg",planets)
cv2.waitKey()
cv2.destroyAllWindows()
上面程序运行后可以得到下方的图像,上一半为原图。
其中关于cv2.HoughCircles参数解析为:
- 第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
- 第二个参数,InputArray类型的lines,检测方法. 目前, 唯一被实现的方法是#HOUGH_GRADIENT
- 第三个参数,以像素为单位的距离精度。另一种形容方式是图像分辨率与累加器分辨率之比dp
- 第四个参数,直线搜索时的进步尺寸的单位minDist。被检测到的圆心的最小距离. 如果该参数很小, 除了一个正确的圆之外, 该圆的邻居也可能被错误地检测出来. 如果该参数很大, 一些圆将可能被错过
- 第五个参数,int类型的param1,第一个指定方法参数的参数. 在#HOUGH_GRADIENT的情况下,其为传递给Canny边缘检测的两个阈值中较高的阈值(较小的阈值为1/2)
- 第六个参数,param2有默认值0。在#HOUGH_GRADIENT的情况下, 它是检测阶段圆心的累加器阈值该值越小, 越多错误的圆将被检测出来. 在投票中获得高票的圆将被先返回.
- 第七个和第八个参数分别为最小圆圈半径和最大圆圈半径,如果maxRadius圆圈半径为0,那么将是最大的圆半径;如果小于0,则返回圆心位置。
而其中的circles:
- circles[0][0]将返回x坐标;
- circles[0][1]将返回y坐标;
- circles[0][2]返回半径。
实例
霍夫圆变换可以根据需求自动调整:
import cv2
import numpy as np
import sys
def onTrackbarChange(max_slider):
cimg = np.copy(img)
p1 = max_slider
p2 = max_slider * 0.4
# Detect circles using HoughCircles transform
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, cimg.shape[0]/64, param1=p1, param2=p2, minRadius=25, maxRadius=50)
# If at least 1 circle is detected
if circles is not None:
cir_len = circles.shape[1] # store length of circles found
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
# Draw the outer circle
cv2.circle(cimg, (i[0], i[1]), i[2], (0, 255, 0), 2)
# Draw the center of the circle
cv2.circle(cimg, (i[0], i[1]), 2, (0, 0, 255), 3)
else:
cir_len = 0 # no circles detected
# Display output image
cv2.imshow('Image', cimg)
# Edge image for debugging
edges = cv2.Canny(gray, p1, p2)
cv2.imshow('Edges', edges)
if __name__ == "__main__":
# Read image
img = cv2.imread(r"brown-eyes.jpg", 1)
# Convert to gray-scale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Create display windows
cv2.namedWindow("Edges")
cv2.namedWindow("Image")
# Trackbar will be used for changing threshold for edge
initThresh = 105
maxThresh = 200
# Create trackbar
cv2.createTrackbar("Threshold", "Image", initThresh, maxThresh, onTrackbarChange)
onTrackbarChange(initThresh)
while True:
key = cv2.waitKey(1)
if key == 27:
break
cv2.destroyAllWindows()
同样,还有线变换:
import cv2
import numpy as np
import sys
def onTrackbarChange(max_slider):
global img
global dst
global gray
dst = np.copy(img)
th1 = max_slider
th2 = th1 * 0.4
edges = cv2.Canny(img, th1, th2)
# Apply probabilistic hough line transform
lines = cv2.HoughLinesP(edges, 2, np.pi/180.0, 50, minLineLength=10, maxLineGap=100)
# Draw lines on the detected points
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(dst, (x1, y1), (x2, y2), (0,0,255), 1)
cv2.imshow("Result Image", dst)
cv2.imshow("Edges",edges)
if __name__ == "__main__":
# Read image
img = cv2.imread('lanes.jpg')
# Create a copy for later usage
dst = np.copy(img)
# Convert image to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Create display windows
cv2.namedWindow("Edges")
cv2.namedWindow("Result Image")
# Initialize threshold value
initThresh = 500
# Maximum threshold value
maxThresh = 1000
cv2.createTrackbar("threshold", "Result Image", initThresh, maxThresh, onTrackbarChange)
onTrackbarChange(initThresh)
while True:
key = cv2.waitKey(1)
if key == 27:
break
cv2.destroyAllWindows()
参考与推荐:
[2]. Opencv学习笔记-----霍夫变换直线检测及原理理解