[Python从零到壹] 六十五.图像识别及经典案例篇之图像霍夫变换详解

Posted Eastmount

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Python从零到壹] 六十五.图像识别及经典案例篇之图像霍夫变换详解相关的知识,希望对你有一定的参考价值。

欢迎大家来到“Python从零到壹”,在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界。所有文章都将结合案例、代码和作者的经验讲解,真心想把自己近十年的编程经验分享给大家,希望对您有所帮助,文章中不足之处也请海涵。Python系列整体框架包括基础语法10篇、网络爬虫30篇、可视化分析10篇、机器学习20篇、大数据分析20篇、图像识别30篇、人工智能40篇、Python安全20篇、其他技巧10篇。您的关注、点赞和转发就是对秀璋最大的支持,知识无价人有情,希望我们都能在人生路上开心快乐、共同成长。

该系列文章主要讲解Python OpenCV图像处理和图像识别知识,前期主要讲解图像处理基础知识、OpenCV基础用法、常用图像绘制方法、图像几何变换等,中期讲解图像处理的各种运算,包括图像点运算、形态学处理、图像锐化、图像增强、图像平滑等,后期研究图像识别、图像分割、图像分类、图像特效处理以及图像处理相关应用。

第一部分作者介绍了图像处理基础知识,第二部分介绍了图像运算和图像增强,接下来第三部分我们将详细讲解图像识别及图像处理经典案例,该部分属于高阶图像处理知识,能进一步加深我们的理解和实践能力。在数字图像处理中,有两个经典的变换被广泛应用——傅里叶变换和霍夫变换。其中,傅里叶变换主要是将时间域上的信号转变为频率域上的信号,用来进行图像除噪、图像增强等处理;霍夫变换主要用来辨别找出物件中的特征,用来进行特征检测、图像分析、数位影像处理等处理。本文主要讲解图像霍夫变换,希望文章对您有所帮助,如果有不足之处,还请海涵。

文章目录

下载地址:记得点赞喔 O(∩_∩)O

前文赏析:(尽管该部分占大量篇幅,但我舍不得删除,哈哈!)

第一部分 基础语法

第二部分 网络爬虫

第三部分 数据分析和机器学习

第四部分 Python图像处理基础

第五部分 Python图像运算和图像增强

第六部分 Python图像识别和图像高阶案例

第七部分 NLP与文本挖掘

第八部分 人工智能入门知识

第九部分 网络攻防与AI安全

第十部分 知识图谱构建实战

扩展部分 人工智能高级案例

作者新开的“娜璋AI安全之家”将专注于Python和安全技术,主要分享Web渗透、系统安全、人工智能、大数据分析、图像识别、恶意代码检测、CVE复现、威胁情报分析等文章。虽然作者是一名技术小白,但会保证每一篇文章都会很用心地撰写,希望这些基础性文章对你有所帮助,在Python和安全路上与大家一起进步。


一.霍夫变换

霍夫变换(Hough Transform)是一种特征检测(Feature Extraction),被广泛应用在图像分析、计算机视觉以及数位影像处理。 霍夫变换是在1959年由气泡室(Bubble Chamber)照片的机器分析而发明,发明者Paul Hough在1962年获得美国专利。现在广泛使用的霍夫变换是由Richard Duda和Peter Hart在1972年发明,并称之为广义霍夫变换。经典的霍夫变换是检测图片中的直线,之后,霍夫变换不仅能识别直线,也能够识别任何形状,常见的有圆形、椭圆形。1981年,因为Dana H.Ballard的一篇期刊论文“Generalizing the Hough transform to detect arbitrary shapes”,让霍夫变换开始流行于计算机视觉界。

霍夫变换是一种特征提取技术,用来辨别找出物件中的特征,其目的是通过投票程序在特定类型的形状内找到对象的不完美实例。这个投票程序是在一个参数空间中进行的,在这个参数空间中,候选对象被当作所谓的累加器空间中的局部最大值来获得,累加器空间是由计算霍夫变换的算法明确地构建。霍夫变换主要优点是能容忍特征边界描述中的间隙,并且相对不受图像噪声的影响。

最基本的霍夫变换是从黑白图像中检测直线,它的算法流程大致如下:给定一个物件和要辨别的形状的种类,算法会在参数空间中执行投票来决定物体的形状,而这是由累加空间里的局部最大值来决定。假设存在直线公式如(1)所示,其中m表示斜率,b表示截距。

如果用参数空间表示,则直线为(b, m),但它存在一个问题,垂直线的斜率不存在(或无限大),使得斜率参数m值接近于无限。为此,为了更好的计算,Richard O. Duda和Peter E. Hart在1971年4月,提出了Hesse normal form(Hesse法线式),如公式(2)所示,它转换为直线的离散极坐标公式。

其中r是原点到直线上最近点的距离,θ是x轴与连接原点和最近点直线之间的夹角,如图1所示。

对于点(x0, y0),可以将通过这个点的一族直线统一定义为公式(3)。因此,可以将图像的每一条直线与一对参数(r,θ)相关联,相当于每一对(r0,θ)代表一条通过点的直线(x0, y0),其中这个参数(r,θ)平面被称为霍夫空间。

然而在实现的图像处理领域,图像的像素坐标P(x, y)是已知的,而(r,θ)是需要寻找的变量。如果能根据像素点坐标P(x, y)值绘制每个(r,θ)值,那么就从图像笛卡尔坐标系统转换到极坐标霍夫空间系统,这种从点到曲线的变换称为直线的霍夫变换。变换通过量化霍夫参数空间为有限个值间隔等分或者累加格子。

当霍夫变换算法开始,每个像素坐标点P(x, y)被转换到(r,θ)的曲线点上面,累加到对应的格子数据点,当一个波峰出现时候,说明有直线存在。如图2所示,三条正弦曲线在平面相交于一点,该点坐标(r0,θ)表示三个点组成的平面内的直线。这就是使用霍夫变换检测直线的过程,它追踪图像中每个点对应曲线间的交点,如果交于一点的曲线的数量超过了阈值,则认为该交点所代表的参数对(r0,θ)在原图像中为一条直线。

同样的原理,可以用来检测圆,对于圆的参数方程变为如下等式:

其中(a, b)为圆的中心点坐标,r圆的半径。这样霍夫参数空间就变成一个三维参数空间。给定圆半径转为二维霍夫参数空间,变换相对简单,也比较常用。


二.图像霍夫线变换操作

在OpenCV中,霍夫变换分为霍夫线变换和霍夫圆变换,其中霍夫线变换支持三种不同方法——标准霍夫变换、多尺度霍夫变换和累计概率霍夫变换[3]。

  • 标准霍夫变换主要有HoughLines()函数实现。
  • 多尺度霍夫变换是标准霍夫变换在多尺度下的变换,可以通过HoughLines()函数实现。
  • 累计概率霍夫变换是标准霍夫变换的改进,它能在一定范围内进行霍夫变换,计算单独线段的方向及范围,从而减少计算量,缩短计算时间,可以通过HoughLinesP()函数实现。

在OpenCV 中,通过函数HoughLines()检测直线,并且能够调用标准霍夫变换(SHT)和多尺度霍夫变换(MSHT),其函数原型如下所示:

  • lines = HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]])
    – image表示输入的二值图像
    – lines表示经过霍夫变换检测到直线的输出矢量,每条直线为(r,θ)
    – rho表示以像素为单位的累加器的距离精度
    – theta表示以弧度为单位的累加器角度精度
    – threshold表示累加平面的阈值参数,识别某部分为图中的一条直线时它在累加平面中必须达到的值,大于该值线段才能被检测返回
    – srn表示多尺度霍夫变换中rho的除数距离,默认值为0。粗略的累加器进步尺寸为rho,而精确的累加器进步尺寸为rho/srn
    – stn表示多尺度霍夫变换中距离精度theta的除数,默认值为0,。如果srn和stn同时为0,使用标准霍夫变换
    – min_theta表示标准和多尺度的霍夫变换中检查线条的最小角度。必须介于0和max_theta之间
    – max_theta表示标准和多尺度的霍夫变换中要检查线条的最大角度。必须介于min_theta和π之间

下面的代码是调用HoughLines()函数检测图像中的直线,并将所有的直线绘制于原图像中。

# -*- coding: utf-8 -*-
# By: Eastmount
import cv2
import numpy as np
from matplotlib import pyplot as plt
import matplotlib

#读取图像
img = cv2.imread('lines.png')

#灰度变换
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#转换为二值图像
edges = cv2.Canny(gray, 50, 150)

#显示原始图像
plt.subplot(121), plt.imshow(edges, 'gray'), plt.title(u'(a)原始图像')

plt.axis('off')

#霍夫变换检测直线
lines = cv2.HoughLines(edges, 1, np.pi / 180, 160)

#转换为二维
line = lines[:, 0, :] 

#将检测的线在极坐标中绘制 
for rho,theta in line[:]: 
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    print(x0, y0)
    x1 = int(x0 + 1000 * (-b))
    y1 = int(y0 + 1000 * (a))
    x2 = int(x0 - 1000 * (-b))
    y2 = int(y0 - 1000 * (a))
    print(x1, y1, x2, y2)
    #绘制直线
    cv2.line(img, (x1, y1), (x2, y2), (255, 0, 0), 1)

#设置字体
matplotlib.rcParams['font.sans-serif']=['SimHei']

#显示处理图像
plt.subplot(122), plt.imshow(img, 'gray'), plt.title('(b)结果图像')

plt.axis('off')
plt.show()

输出结果如图3所示,第一幅图为原始图像,第二幅检测出的直线。

使用该方法检测大楼图像中的直线如图4所示,可以发现直线会存在越界的情况。

前面的标准霍夫变换会计算图像中的每一个点,计算量比较大,另外它得到的是整条线(r,θ),并不知道原图中直线的端点。接下来使用累计概率霍夫变换,它是一种改进的霍夫变换,调用HoughLinesP()函数实现。

  • lines = HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]])
    – image表示输入的二值图像
    – lines表示经过霍夫变换检测到直线的输出矢量,每条直线具有4个元素的矢量,即(x1, y1)和(x2, y2)是每个检测线段的端点
    – rho表示以像素为单位的累加器的距离精度
    – theta表示以弧度为单位的累加器角度精度
    – threshold表示累加平面的阈值参数,识别某部分为图中的一条直线时它在累加平面中必须达到的值,大于该值线段才能被检测返回
    – minLineLength表示最低线段的长度,比这个设定参数短的线段不能被显示出来,默认值为0
    – maxLineGap表示允许将同一行点与点之间连接起来的最大距离,默认值0

下面的代码是调用HoughLinesP()函数检测图像中的直线,并将所有的直线绘制于原图像中。

# -*- coding: utf-8 -*-
# By: Eastmount
import cv2
import numpy as np
from matplotlib import pyplot as plt
import matplotlib

#读取图像
img = cv2.imread('judge.png')

#灰度转换
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#转换为二值图像
edges = cv2.Canny(gray, 50, 200)

#显示原始图像
plt.subplot(121), plt.imshow(edges, 'gray'), plt.title(u'(a)原始图像')
plt.axis('off')

#霍夫变换检测直线
minLineLength = 60
maxLineGap = 10
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 30, minLineLength, maxLineGap)

#绘制直线
lines1 = lines[:, 0, :]
for x1,y1,x2,y2 in lines1[:]:
    cv2.line(img, (x1,y1), (x2,y2), (255,0,0), 2)

res = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

#设置字体
matplotlib.rcParams['font.sans-serif']=['SimHei']

#显示处理图像
plt.subplot(122), plt.imshow(res), plt.title('(b)结果图像')
plt.axis('off')
plt.show()

输出结果如图5所示,图(a)为原始图像,图(b)检测出的直线,它有效地提取了线段的起点和终点。


三.图像霍夫圆变换操作

霍夫圆变换的原理与霍夫线变换很类似,只是将线的(r,θ)二维坐标提升为三维坐标,包括圆心点(x_center,y_center,r)和半径r,其数学形式如公式(5)。

从而一个圆的确定需要三个参数,通过三层循环实现,接着寻找参数空间累加器的最大(或者大于某一阈值)的值。随着数据量的增大,导致圆的检测将比直线更耗时,所以一般使用霍夫梯度法减少计算量。在OpenCV中,提供了cv2.HoughCircles()函数检测圆,其原型如下所示:

  • circles = HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]])
    – image表示输入图像,8位灰度单通道图像
    – circles表示经过霍夫变换检测到圆的输出矢量,每个矢量包括3个元素,即(x, y, radius)
    – method表示检测方法,包括HOUGH_GRADIENT值
    – dp表示用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,允许创建一个比输入图像分辨率低的累加器
    – minDist表示霍夫变换检测到的圆的圆心之间的最小距离
    – param1表示参数method设置检测方法的对应参数,对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示传递给Canny边缘检测算子的高阈值,而低阈值为高阈值的一半,默认值100
    – param2表示参数method设置检测方法的对应参数,对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值,它越小,将检测到更多根本不存在的圆;它越大,能通过检测的圆就更接近完美的圆形
    – minRadius表示圆半径的最小值,默认值为0
    – maxRadius表示圆半径的最大值,默认值0

下列代码是检测图像中的圆。

# -*- coding: utf-8 -*-
# By: Eastmount
import cv2
import numpy as np
from matplotlib import pyplot as plt
import matplotlib

#读取图像
img = cv2.imread('test.png')

#灰度转换
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#显示原始图像
plt.subplot(121), plt.imshow(gray, 'gray'), plt.title(u'(a)原始图像')
plt.axis('off')

#霍夫变换检测圆
circles1 = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20, param2=30)
print(circles1)

#提取为二维
circles = circles1[0, :, :]

#四舍五入取整
circles = np.uint16(np.around(circles))

#绘制圆
for i in circles[:]: 
    cv2.circle(img, (i[0],i[1]), i[2], (255,0,0), 5) #画圆
    cv2.circle(img, (i[0],i[1]), 2, (255,0,255), 10) #画圆心

#设置字体
matplotlib.rcParams['font.sans-serif']=['SimHei']
plt.subplot(122), plt.imshow(img), plt.title('(b)结果图像')
plt.axis('off')
plt.show()

输出结果如图6所示,图(a)为原始图像,图(b)检测出的圆形,它有效地提取了圆形的圆心和轮廓。

使用下面的函数能有效提取人类眼睛的轮廓,核心函数如下:

circles1 = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20,
                            param1=100, param2=30,
                            minRadius=160, maxRadius=300)

输出结果如图7所示,它提取了三条圆形接近于人体的眼睛。

图7中显示了三条曲线,通过不断优化最大半径和最小半径,比如将minRadius设置为160,maxRadius设置为200,将提取更为精准的人体眼睛,如图8所示。


四.总结

本章主要讲解霍夫变换。霍夫变换主要用来辨别找出物件中的特征,包括提取图像中的直线和圆,调用cv2.HoughLines()、cv2.HoughLinesP()和cv2.HoughCircles()实现。希望对大家有所帮助,这也将为后续图像识别和目标检测提供帮助。

感谢在求学路上的同行者,不负遇见,勿忘初心。图像处理系列主要包括三部分,分别是:

转眼就过年了,2022年简单总结:很多遗憾,很多不足,勉强算是分秒必争,只争朝夕,但愧对家人,陪伴太少,论文、科研、分享和家庭都做得不好,这一年勉强给个65分吧。最最感恩的永远是女神,回家的感觉真好,平平淡淡,温温馨馨,虽然这辈子科研、事业和职称上没有太大的追求,和大佬们的差距如鸿沟,但能做自己喜欢的事,爱自己喜欢的人,每天前进一小步(人生勿比),一家人健康幸福,足矣。小珞珞是真的逗,陪伴的感觉真好,女神是真的好,爱你们喔,晚安娜继续加油!

参考文献:

  • [1]冈萨雷斯著. 数字图像处理(第3版)[M]. 北京:电子工业出版社,2013.
  • [2]阮秋琦. 数字图像处理学(第3版)[M]. 北京:电子工业出版社,2008.
  • [3]毛星云,冷雪飞. OpenCV3编程入门[M]. 北京:电子工业出版社,2015.
  • [4]张铮,王艳平,薛桂香等. 数字图像处理与机器视觉——Visual C++与Matlab实现[M]. 北京:人民邮电出版社,2014.
  • [5]Eastmount. [Python图像处理] 三十二.傅里叶变换(图像去噪)与霍夫变换(特征识别)万字详细总结[EB/OL]. (2020.12.02). https://blog.csdn.net/Eastmount/article/details/ 110487868.
  • [6]百度百科. 霍夫变换[EB/OL]. (2018-11-21). https://baike.baidu.com/item/霍夫变换/4647236.
  • [7]yuyuntan. 经典霍夫变换(Hough Transform)[EB/OL]. (2018-04-29). https://blog.csdn.net/yuyuntan/article/details/80141392.
  • [8]g20040733. 霍夫变换[EB/OL]. (2016-12-07). https://blog.csdn.net/g200407331/article/details/53507784.
  • [9]我i智能. Python下opencv使用笔记(十一)(详解hough变换检测直线与圆)[EB/OL]. (2015-07-23). https://blog.csdn.net/on2way/article/details/47028969.
  • [10]ex2tron. Python+OpenCV教程17:霍夫变换[EB/OL]. (2018-01-10). https://www.jianshu.com/p/34d6dc466e81.
  • [11]Daetalus. OpenCV-Python教程(9、使用霍夫变换检测直线)[EB/OL]. (2013-07-12). https://blog.csdn.net/sunny2038/article/details/9253823.

以上是关于[Python从零到壹] 六十五.图像识别及经典案例篇之图像霍夫变换详解的主要内容,如果未能解决你的问题,请参考以下文章

[Python从零到壹] 六十三.图像识别及经典案例篇之图像漫水填充分割应用

[Python从零到壹] 六十三.图像识别及经典案例篇之图像漫水填充分割应用

[Python从零到壹] 六十一.图像识别及经典案例篇之基于纹理背景和聚类算法的图像分割

[Python从零到壹] 六十一.图像识别及经典案例篇之基于纹理背景和聚类算法的图像分割

[Python从零到壹] 六十二.图像识别及经典案例篇之基于均值漂移算法和分水岭算法的图像分割

[Python从零到壹] 六十二.图像识别及经典案例篇之基于均值漂移算法和分水岭算法的图像分割