Python 计算机视觉编程进阶之OpenCV 进行霍夫变换
作者:一马归一码
参考的一些文章以及论文我都会给大家分享出来 —— 链接就贴在原文,论文我上传到资源中去,大家可以免费下载学习,如果当天资源区找不到论文,那就等等,可能正在审核,审核完后就可以下载了。大家一起学习,一起进步!加油!!
前言
(1)读取图像信息
经典操作,不必多说:
""" Author:XiaoMa date:2021/11/13 """ import cv2 import matplotlib.pyplot as plt import numpy as np img0 = cv2.imread("E:\From Zhihu\For the desk\cvfourteen1.jpg") img1 = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY) h, w = img0.shape[:2] print(h, w) cv2.imshow("W0", img0) cv2.imshow("W1", img1) cv2.waitKey(delay = 0)
525 787
(2)霍夫变换的目的及应用
经典的霍夫变换是被用来检测图像中的直线,后来扩展到任何形状的检测识别中,多为直线和椭圆。所以利用霍夫变换我们可以提取图像中的直线以及其他形状类型的线条。
1. 霍夫变换
霍夫变换是图像处理中的一种特征提取技术,它通过一种投票算法检测具有特定形状的物体。该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换的结果。
霍夫变换运用两个坐标空间的变换将在一个空间中具有相同形状的曲线或者直线映射到另外一个空间的坐标点上形成峰值,从而把检测任意形状的问题转化为统计峰值的问题。
2. 霍夫线变换
(1)基本概念
我们一般理解的在笛卡尔坐标系中表示直线的方式有点线式和两点式,然而在霍夫变换中,考虑的却是另外一种方式:使用 (r, theta) 表示一条直线,其中 r 代表原点到这条直线的距离,theta 表示该直线的垂线与 x 轴的夹角。
那么我们怎么检测直线呢?首先我们为每一个点假设 n 个方向的直线,一般 n 为180,这样检测直线的角度精度为 1 度,分别计算这 n 条直线的(r, theta)坐标,得到 n 个坐标点。那么判断 N 个坐标点就得到 N*n 个 (r, theta)坐标,其中 theta 是离散的角度,有180个取值。当多个点在同一条直线上时,那么这条直线可以通过这些点中的任意一个点的某一个(r_i, theta_i)表示出来(每一个点取到特定的 theta 时得到的 r 相等或者相近)。比如空间中有三个点,下图表示了判断这三个点共线的方式:
可以看出当 Angel(theta) 为 60 时距离 (r) 大致都为80.7,由此可以判断这三个点都在直线(80.7,60)上。
我们也可以绘制一幅 r-theta 坐标系,每一个点的 theta 为横坐标轴,r 为纵坐标,当不同的点出现交点说明这两个点在同一条直线上:
(2)代码实现
本例中使用到了边缘检测来减少计算,如果有小伙伴对边缘检测不熟悉可以去参考我之前的文章:
Python 计算机视觉编程进阶之OpenCV 图像锐化及边缘检测
如果对函数的参数有疑问的话可以参考官网:OpenCV
对于下面的代码,我都添加注释了,应该讲清楚了,如果有注释的不清楚的地方,可以在评论区指出来,大家一起讨论
#霍夫直线检测 ##首先进行边缘检测,来减少空间中其他的点带来的计算量的问题 img2 = cv2.GaussianBlur(img1, (5, 5), 0) #高斯模糊为边缘检测做准备 img3 = cv2.Canny(img2, 50, 120) #使用Canny算子进行边缘检测 cv2.imshow("W2", img3) cv2.waitKey(delay = 0) rho = 1 #距离分辨率 theta = np.pi/180 #角度分辨率 threshold = 10 #霍夫空间中多少个曲线相交表示一个正式的交点 min_line_len = 50 #最少需要多少个像素点才构成一条直线 max_line_gap = 50 #线段之间的最大间隔像素点数 lines = cv2.HoughLinesP(img3, rho, theta, threshold, maxLineGap = max_line_gap) #所以这个函数中的参数都已经在前面赋值时解释过了 img4 = np.zeros_like(img3) for line in lines: for x1, y1, x2, y2 in line: cv2.line(img4, (x1, y1), (x2, y2), 255, 1) #绘制直线 cv2.imshow("W3", img4) cv2.waitKey(delay = 0)
得到的边缘检测图像为:
得到的霍夫直线检测的图像为:
当然霍夫直线检测用在其他的比如建筑等方面才是好钢用在刀刃上,这里作为示范用了人像。
3. 霍夫圆变换
(1)基本概念
一般来说,表示一个圆需要知道它的半径以及圆心,这样我们需要 (x, y, r) 三个参数,如果只是靠这种方法识别圆,那么对于计算机来说计算效率会下降。
在 OpenCV 中是使用了霍夫梯度的方法,利用边界梯度信息。首先使用 Canny() 进行边缘检测,对边缘的每一个非 0 通过 Sobel() 进行局部梯度计算(Sobel() 算子也在前面的文章中介绍过了),得到的梯度方向就是圆切线的方向,得到3个切线就可以确定圆心了。
(2)代码实现
已添加注释:
#霍夫圆检测 dp = 1 #检测内测圆心的累加器图像的分辨率与输入图像之比的倒数 minDist = 700 #两个圆之间圆心之间的最小距离 param1 = 100 #前面提到过 Canny 边缘检测,这个参数表示传递给边缘检测的高阈值 param2 = 80 #检测阶段圆心的累加器阈值,简单来说该参数越大检测到的圆越完美,但数目越少,反之亦然 minRadius = 10 #最小圆的半径 maxRadius = 20 #最大圆的半径 cirlces = cv2.HoughCircles(img2, cv2.HOUGH_GRADIENT, dp, minDist, param1, param2, minRadius, maxRadius) #函数的参数前面解释过了 cirlces = np.uint16(np.around(cirlces)) for i in cirlces[0, :]: cv2.circle(img0, (i[0], i[1]), i[2], (0, 0, 255), 1) cv2.circle(img0, (i[0], i[1]), 2, (0, 0, 255), 1) cv2.imshow("W4", img0) cv2.waitKey(delay = 0)
可以看出对人像的检测局限于头部,哈哈哈哈,建议大家多改几次参数进行尝试,会得到不一样的体验,也可以多换几张图进行测试。
4. 将所有图像绘制到一张图中
都是一些常规问题,没啥好注释的
#将所有图像绘制在一张图纸上 img0 = cv2.imread("E:\From Zhihu\For the desk\cvfourteen1.jpg") #再次读取原图,前面的图像已经进行了变换 plt.rcParams['font.family'] = 'SimHei' #将全局中文字体改为黑体 imgs = [img0, img1, img2, img3, img4, img5] title = ['原图', '灰度图', '高斯模糊', '边缘检测', '霍夫直线检测', '霍夫圆检测'] for i in range(6): imgs[i] = cv2.cvtColor(imgs[i], cv2.COLOR_BGR2RGB) plt.subplot(2, 3, i + 1) plt.imshow(imgs[i]) plt.title(title[i]) plt.xticks([]) plt.yticks([]) plt.show()
5. 总体代码
修改图像路径就可以用了,但还是建议小伙伴们一步步来。
""" Author:XiaoMa date:2021/11/13 """ import cv2 import matplotlib.pyplot as plt import numpy as np img0 = cv2.imread("E:\From Zhihu\For the desk\cvfourteen1.jpg") #img0 = cv2.resize(img0, dsize = None, fx = 0.5, fy = 0.5) img1 = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY) h, w = img0.shape[:2] print(h, w) cv2.imshow("W0", img0) cv2.imshow("W1", img1) cv2.waitKey(delay = 0) #霍夫直线检测 ##首先进行边缘检测,来减少空间中其他的点带来的计算量的问题 img2 = cv2.GaussianBlur(img1, (5, 5), 0) #高斯模糊为边缘检测做准备 img3 = cv2.Canny(img2, 50, 120) #使用Canny算子进行边缘检测 cv2.imshow("W2", img3) cv2.waitKey(delay = 0) rho = 1 #距离分辨率 theta = np.pi/180 #角度分辨率 threshold = 10 #霍夫空间中多少个曲线相交表示一个正式的交点 min_line_len = 50 #最少需要多少个像素点才构成一条直线 max_line_gap = 50 #线段之间的最大间隔像素点数 lines = cv2.HoughLinesP(img3, rho, theta, threshold, maxLineGap = max_line_gap) #所以这个函数中的参数都已经在前面赋值时解释过了 img4 = np.zeros_like(img3) for line in lines: for x1, y1, x2, y2 in line: cv2.line(img4, (x1, y1), (x2, y2), 255, 1) #绘制直线 cv2.imshow("W3", img4) cv2.waitKey(delay = 0) #霍夫圆检测 dp = 1 #检测内测圆心的累加器图像的分辨率与输入图像之比的倒数 minDist = 700 #两个圆之间圆心之间的最小距离 param1 = 100 #前面提到过 Canny 边缘检测,这个参数表示传递给边缘检测的高阈值 param2 = 80 #检测阶段圆心的累加器阈值,简单来说该参数越大检测到的圆越完美,但数目越少,反之亦然 minRadius = 10 #最小圆的半径 maxRadius = 20 #最大圆的半径 cirlces = cv2.HoughCircles(img2, cv2.HOUGH_GRADIENT, dp, minDist, param1, param2, minRadius, maxRadius) #函数的参数前面解释过了 cirlces = np.uint16(np.around(cirlces)) img5 = img0 for i in cirlces[0, :]: cv2.circle(img5, (i[0], i[1]), i[2], (0, 0, 255), 1) cv2.circle(img5, (i[0], i[1]), 2, (0, 0, 255), 1) cv2.imshow("W4", img5) cv2.waitKey(delay = 0) #将所有图像绘制在一张图纸上 img0 = cv2.imread("E:\From Zhihu\For the desk\cvfourteen1.jpg") #再次读取原图,前面的图像已经进行了变换 plt.rcParams['font.family'] = 'SimHei' #将全局中文字体改为黑体 imgs = [img0, img1, img2, img3, img4, img5] title = ['原图', '灰度图', '高斯模糊', '边缘检测', '霍夫直线检测', '霍夫圆检测'] for i in range(6): imgs[i] = cv2.cvtColor(imgs[i], cv2.COLOR_BGR2RGB) plt.subplot(2, 3, i + 1) plt.imshow(imgs[i]) plt.title(title[i]) plt.xticks([]) plt.yticks([]) plt.show()
结束语
本文主要总结了霍夫变换的基本概念和代码实现,包括线变换和圆变换,建议大家多尝试,一些参数什么的多修改多试试。本篇文章主要参考的是《Python计算机视觉》这边书,清华大学出版的,大家感兴趣可以找来看看。
到此这篇关于Python 计算机视觉编程进阶之OpenCV 进行霍夫变换的文章就介绍到这了,更多相关Python OpenCV 霍夫变换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!