CV15 轮廓检测:霍夫变换原理及应用

1.基本原理

1.1数学分析

  • 如下图,y=kx+q

cv151

我们可以把k看作自变量,把q看作因变量,则有:
A%3A%20q%3D-kx_1%2By_1%5C%5C%20B%3A%20q%3D-kx_2%2By_2
这个过程称为霍夫变换,在霍夫空间上产生一个点,如下图所示:

CV15

可以看出,坐标系的直线经过霍夫变换后成为霍夫空间上的一个点,如下图所示:

CV153

  • 若有A(x1,y1) 、B(x2,y2),且x1!=x2,y1!=y2

%E5%BD%93q_A%3Dq_B%2Ck_A%3Dm%5Ctimes%20K_B%5C%5C%20-x_1%5Ccdot%20k%20%2B%20y1%20%3D%20-x_2%5Ccdot%20k%20%2B%20y_2%5C%5C
有交点Q,在交点Q处,x1=x2,y1=y2

此时,可以说明A/B在同一条直线上

k变换,q也就随之变换。这个过程表示经过D点的直线,360°旋转,如下图:

  • 多点共线的情况,如下图:

sadsad

图2中虽然有多个交点,但我们主要关注三线交点的情况,这也是霍夫变换后处理的基本方式:选择尽可能多的直线形成的点

  • 但是仅用k、q表示霍夫空间是有问题的,因为我们忽视了一种特殊情况:直线与x轴垂直的情况

ddfdsf

k=∞是不方便表示的,所以我们必须改变一下坐标系:用极坐标表示点,线
x_1%3D%5Crho%20%3Dcos%5Ctheta%5C%5C%20y_1%3D%5Crho%20sin%5Ctheta%5C%5C%20%5Crho%20%3Dxcos%5Ctheta%2Bysin%5Ctheta%5C%5C%20%E6%89%80%E4%BB%A5%EF%BC%8C%E5%8F%AF%E7%94%A8%5B%5Crho%2C%5Ctheta%5D%E8%A1%A8%E7%A4%BA%E4%B8%80%E4%B8%AA%E7%82%B9
霍夫空间也发生了变化,但点的曲线发生了变化。

k4Rn9P

1.2图像处理应用

那么我们应该如何在图像处理中使用它呢?

  • 假设我们图像中有一段有8个像素点组成直线
  • 1.建立直角坐标系
  • 2.将第一个像素点的(x,y)代入公式中%5Crho%20%3Dxcos%5Ctheta%2Bysin%5Ctheta%5C%5C
  • 3.此时角度作为自变量,我们选用遍历查询的办法,不断旋转增加角度(记住,角度增加量极小,但下图为了方便计算,我每次旋转都增加了45°)
  • 4.在(1,8)处有5个ρ值,我们将结果记录下来
  • 5.然后计算(3,6)坐标处,依此类推,也将结果记录下来
  • 6.每个像素点的旋转的数据都记录下来之后,找哪一个ρ值出现的最多(下图是 (9√2)/2)
  • 7.再次带(9√2)/2到公式(4)中,得到角度
  • 8.计算出**[ρ,θ]**,即可表示出一条线了

6

以上就是标准霍夫变换的原理

但是我们一般不使用标准霍夫变换,而是概率霍夫变换😂😂😂

参考资料:霍夫变换直线检测(Line Detection)原理及示例

​(48)通俗易懂——霍夫变换原理

2.概率霍夫变换

2.1标准霍夫与概率霍夫

  • 标准霍夫变换
    :把图像映射到它的参数空间上,它需要计算所有的M个边缘点,这样它的运算量和所需内存空间都会很大。
  • 概率霍夫变换
    :如果在输入图像中只是处理m(m

2.2概率霍夫检测步骤

  1. 随机抽取图像中的一个特征点,即边缘点。如果该点已经被标记为一条直线上的点,则继续从剩余的边缘点中随机抽取一个边缘点,直到提取完所有的边缘点。直到;
  2. 对点进行霍夫变换,进行累加计算;
  3. 选取在霍夫空间内值最大的点,如果该点大于阈值的,则进行步骤4,否则回到步骤1;
  4. 根据霍夫变换得到的最大值,从该点开始,沿着直线的方向移动,找到直线的两个端点;
  5. 计算直线的长度,如果大于某个阈值,则被认为是好的直线输出,回到步骤1。

3.霍夫变换检测线

3.1cv2.HoughLinesP()函数

HoughLinesP函数就是利用概率霍夫变换来检测直线的

cv2.HoughLinesP()功能

功能:概率霍夫变换检测直线

输入参数:

  1. image:图像名,
    强烈建议输入经Canny检测之后的图像
  2. rho:搜索线时的位置距离间隔(以像素为单位)
  3. theta:搜索线时的旋转角度差(以度为单位)
  4. threshold:表示丢弃长度低于该阈值的线,显然这个值越大,所判断出的直线越少;这个值越小,所判断出的直线越多
  5. minLineLength:设置最小线段长度,一次性丢弃较短的线
  6. maxLineGap:最大直线间隙,即如果有两条线段在一条直线上,但它们之间因为有间隙,所以被认为是两个线段,如果这个间隙大于该值,则被认为是两条线段,否则是一条

返回值:lines为输出的直线向量,每条线用4个元素表示,即直线的两个端点的4个坐标值(x1, y1, x2, y2)表示,其中(x1, y1)表示线段的起点,(x2, y2)表示线段的终点

例:lines = cv2.HoughLinesP(edges,1,np.pi/180,30,minLineLength,maxLineGap)

3.2线检测程序

import cv2 as cv
import numpy as np
img = cv.imread('test2.jpg')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
edges = cv.Canny(gray,50,150,apertureSize = 3)
lines = cv.HoughLinesP(edges,1,np.pi/180,100,minLineLength=100,maxLineGap=10)
for line in lines:
  x1,y1,x2,y2 = line[0]
  cv.line(img,(x1,y1),(x2,y2),(0,255,0),2)
cv.imshow('houghlines5.jpg',img)
cv.waitKey()
cv.destroyAllWindows()

houghlines5

注意! ! ! :

市面上有些教科书是这样写的,拿不出理论结果💔

fasdf

正确答案是:

for line in lines:
  x1,y1,x2,y2 = line[0]
  cv.line(img,(x1,y1),(x2,y2),(0,255,0),2)

4.霍夫变换检测圆

Opencv中还有一个函数cv2.HoughCircles,它通过霍夫梯度法,实现了圆的检测

参考资料:opencv —— HoughCircles 霍夫圆变换原理及圆检测

4.1cv2.HoughCircles()函数

cv2.HoughCircles()功能

功能:霍夫变换检测圆

输入参数:

  1. img: 待检测的
    灰度
  2. cv2.HOUGH_GRADIENT:检测的方法,霍夫梯度,也是
    只要
    检测方法
  3. 1:检测的圆与原始图像具有相同的大小,dp=2,检测的圆是原始图像的一半
  4. 20:检测到的
    相邻圆心之间的最小距离
    (参数过小,除了一个真圆外,可能会误检测出多个相邻的圆。过大,可能会漏掉一些圆。)
  5. param1:Canny 边缘检测的高阈值,低阈值被自动置为高阈值的一半,默认为 100
    (霍夫梯度法里面已存在Canny检测,故我们无需重复)
  6. param2:
    被电脑判断为圆的难度
    .它越小,越有可能检测到错误的圆圈;
    并且越大,可以通过测试的圆越接近一个完美的圆。
  7. minRadius:最小圆半径
  8. maxRadius:最大圆半径,如果<=0,则使用最大图像尺寸。如果<0,则返回没有找到半径的中心。

返回值:cv2.HoughCircles的返回将其reshape为(-1, 3),每一行就是一圆的参数,分别是(圆心横坐标,圆心纵坐标,半径),最后用于该处:

cv2.circle(coins_img, (i[0], i[1]), i[2], (0, 0, 255), 5)   # 画圆

例:circle=cv2.HoughCircles(gray_img,cv2.HOUGH_GRADIENT,1,120,param1=100,param2=30,minRadius=0,maxRadius=0)

4.2圆检测程序

import cv2
import numpy as np

planets = cv2.imread('planet_glow.jpg')
gray_img = cv2.cvtColor(planets,cv2.COLOR_BGR2GRAY)
gray_img = cv2.medianBlur(gray_img,5)

circles = cv2.HoughCircles(gray_img,cv2.HOUGH_GRADIENT,1,120,param1=80,param2=35,minRadius=0,maxRadius=0)
print(circles)
circles = np.uint16(np.around(circles))

for i in circles[0,:]:
    cv2.circle(planets,(i[0],i[1]),i[2],(0,255,0),2)

    cv2.circle(planets,(i[0],i[1]),2,(0,0,255),3)

cv2.imshow('planet',planets)
cv2.waitKey()
cv2.destroyAllWindows()

效果显着! 😎😎😎

planet

5.结语

Opencv入门篇已经学完了,接下来有其它的事情❌,这个专栏预计停更一两周📵

验车牌,看看周三能不能做😭😭😭

忙完之后,估计就到了用Haar级联实现人脸检测🐵

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2022年3月15日 上午11:07
下一篇 2022年3月15日 上午11:29

相关推荐