Python OpenCV 霍夫(Hough Transform)直线变换检测应用

举报
梦想橡皮擦 发表于 2021/09/20 15:00:20 2021/09/20
【摘要】 Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。本篇博客是这个系列的第 34 篇。 基础知识铺垫上篇博客 咱们一起学习了霍夫直线检测的原理,本篇就从应用层对其进行学习啦。学习了原理之后,在查看函数原型,发现确实简单了许多。 霍夫直线变换函数原型在 OpenCV 中提供了两个霍夫直线检测的函数,一个是标准霍夫变换,另一个是概率霍夫变换。先学习一下标准霍夫变换吧,该变...

Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧。本篇博客是这个系列的第 34 篇。

基础知识铺垫

上篇博客 咱们一起学习了霍夫直线检测的原理,本篇就从应用层对其进行学习啦。

学习了原理之后,在查看函数原型,发现确实简单了许多。

霍夫直线变换函数原型

在 OpenCV 中提供了两个霍夫直线检测的函数,一个是标准霍夫变换,另一个是概率霍夫变换。

先学习一下标准霍夫变换吧,该变化方式也叫做多尺度霍夫变换。

该方法使用的函数是 cv2.HoughLines,函数原型如下

lines = cv2.HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]])

参数说明:

  • image:输入 8 位灰度图像;
  • rho:生成极坐标时像素扫描步长;
  • theta:生成极坐标时候的角度步长;
  • threshold:阈值;
  • lines:返回值,极坐标表示的直线;
  • sen:是否应用多尺度的霍夫变换,如果不是设置 0 表示经典霍夫变换;
  • stn:是否应用多尺度的霍夫变换,如果不是设置 0 表示经典霍夫变换;
  • min_theta:角度扫描范围最小值;
  • max_theta:角度扫描范围最大值。

该函数的返回值就是上篇博客提及的 ( θ,ρ ),其中 ρ 的单位是像素, θ 的单位是弧度。

既然是直线检测,那我们先把图像的边缘检测出来,通过之前学习的知识即可,加入滑动条,方便调参。

import cv2 as cv
import numpy as np

src = cv.imread("./331.jpg")
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

def nothing():
    pass

cv.namedWindow("bar")
cv.createTrackbar("threshold1", "bar", 0, 255, nothing)
cv.createTrackbar("threshold2", "bar", 0, 255, nothing)

dst = cv.equalizeHist(gray_img)
# 高斯滤波降噪
gaussian = cv.GaussianBlur(dst, (9, 9), 0)
cv.imshow("gaussian", gaussian)

while True:
    threshold1 = cv.getTrackbarPos("threshold1", "bar")
    threshold2 = cv.getTrackbarPos("threshold2", "bar")
    # 边缘检测
    edges = cv.Canny(gaussian, threshold1, threshold2)
    cv.imshow("edges", edges)
    if cv.waitKey(1) & 0xFF == 27:
        break

cv.destroyAllWindows()

运行代码之后,获取到的边缘如下,下面就可以对直线进行获取了,通过cv2.HoughLines函数。

修改代码如下:

import cv2 as cv
import numpy as np

src = cv.imread("./331.jpg")
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)


dst = cv.equalizeHist(gray_img)
# 高斯滤波降噪
gaussian = cv.GaussianBlur(dst, (9, 9), 0)
# cv.imshow("gaussian", gaussian)

# 边缘检测
edges = cv.Canny(gaussian, 70, 150)
cv.imshow("edges", edges)
# Hough 直线检测
# 重点注意第四个参数 阈值,只有累加后的值高于阈值时才被认为是一条直线,也可以把它看成能检测到的直线的最短长度(以像素点为单位)
# 在霍夫空间理解为:至少有多少条正弦曲线交于一点才被认为是直线
lines = cv.HoughLines(edges, 1.0, np.pi/180, 150)

# 将检测到的直线通过极坐标的方式画出来
print(lines.ndim)
print(lines.shape)
for line in lines:
    # line[0]存储的是点到直线的极径和极角,其中极角是弧度表示的,theta是弧度
    rho, theta = line[0]

    # 下述代码为获取 (x0,y0) 具体值
    a = np.cos(theta)
    b = np.sin(theta)

    x0 = a*rho
    y0 = b*rho

    # 下图 1000 的目的是为了将线段延长
    # 以 (x0,y0) 为基础,进行延长
    x1 = int(x0+1000*(-b))
    y1 = int(y0+1000*a)
    x2 = int(x0-1000*(-b))
    y2 = int(y0-1000*a)

    cv.line(src, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv.imshow("src", src)
cv.waitKey()
cv.destroyAllWindows()

上述代码中,相关说明如下:

  • 第二个参数为半径的步长,第三个参数为每次偏转的角度,即我们提及的 ρθ
  • rho 参数为极径 r, 以像素值为单位, 本案例使用 1 像素;
  • theta参数极角 θ 以弧度为单位,本案例使用 1 度 (即 np.pi/180);
  • threshold 参数随便给的。

还有计算具体坐标用到的公式其实是这样的:(p,q) = (rcosθ,rsinθ)

运行效果基本满意,达到预期。

概率霍夫变换(Probabilistic Hough Transform)

概率霍夫变换是一种概率直线检测,它是针对于上文标准霍夫检测的优化,核心点是采取概率挑选机制,选取一些点出来进行计算,相当于降采样。

函数名称与原型如下:

lines = cv2.HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]])

参数说明:

  • src:输入 8-bit 的灰度图像;
  • rho:像素为单位的距离精度,double 类型的,推荐用 1.0;
  • theta:以弧度为单位的角度精度,推荐用 numpy.pi/180;
  • threshold:阈值;
  • lines:输出的极坐标来表示直线;
  • minLineLength:最短长度阈值,比这个长度短的线会被排除;
  • maxLineGap:最大间隔,如果小于此值,这两条直线就被看成是一条直线。

该函数还有一个优点,返回值是一条条线段,不用我们在进行转换了

修改上文代码如下,参数赋值部分直接写的,你可以自由修改,也可以通过滑动条调参:

import cv2 as cv
import numpy as np

src = cv.imread("./331.jpg")
gray_img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)


dst = cv.equalizeHist(gray_img)
# 高斯滤波降噪
gaussian = cv.GaussianBlur(dst, (9, 9), 0)
# cv.imshow("gaussian", gaussian)

# 边缘检测
edges = cv.Canny(gaussian, 70, 150)
cv.imshow("edges", edges)

lines = cv.HoughLinesP(edges, 1.0, np.pi/180, 100,
                       minLineLength=100, maxLineGap=6)
print(type(lines))
for line in lines:
    x1, y1, x2, y2 = line[0]
    cv.line(src, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv.imshow("src", src)
cv.waitKey()
cv.destroyAllWindows()

运行代码之后,可以获取到相应的线段。

橡皮擦的小节

OpenCV 的文章阶段性的难度开始爬升了,有些地方概念还是有点模糊,需要学习的地方太多了,一起加油吧

希望今天的 1 个小时(今天内容有点多,不一定可以看完),你有所收获,我们下篇博客见~

相关阅读


技术专栏

  1. Python 爬虫 100 例教程,超棒的爬虫教程,立即订阅吧
  2. Python 爬虫小课,精彩 9 讲

今天是持续写作的第 74 / 100 天。
如果你有想要交流的想法、技术,欢迎在评论区留言。


如果你想跟博主建立亲密关系,可以关注同名公众号 梦想橡皮擦,近距离接触一个逗趣的互联网高级网虫。
博主 ID:梦想橡皮擦,希望大家点赞评论收藏

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。