基于霍夫变换的目标检测
- 0. 前言
- 1. 使用圆形霍夫变换统计图像中圆形对象
- 2. 使用渐进概率霍夫变换检测直线
- 2.1 渐进霍夫变换原理
- 2.2 直线检测
- 3. 使用广义霍夫变换检测任意形状的对象
- 3.1 广义霍夫变换原理
- 3.2 检测自定义形状
- 小结
- 系列链接
0. 前言
霍夫变换 (Hough Transform
, HT
) 是一种特征提取技术,旨在使用在参数空间中执行的投票过程来查找特定形状的对象实例。经典的霍夫变换可用于检测图像中的直线:
- 我们可以使用极参数 ( ρ , θ ) (ρ,\theta) (ρ,θ) 表示直线,其中 ρ ρ ρ 是线段的长度, θ θ θ 是线和 x x x 轴之间的夹角
- 为了探索 ( ρ , θ ) (ρ,θ) (ρ,θ) 参数空间,首先在 ρ − θ ρ-θ ρ−θ 空间中创建二维直方图
- 然后,对于 ρ ρ ρ 和 θ θ θ 的每个值,计算输入图像中接近由参数构建的直线的非零像素的数量,并相应地将数组 ( ρ , θ ) (ρ,θ) (ρ,θ) 递增
- 因此,每个非零像素都可以被认为是对潜在候选线的投票
- 最可能的线对应于获得最高投票的参数值,即
2D
直方图中的局部最大值。
可以使用类似的投票过程来查找圆的参数空间中的最大值,从而将该方法扩展到检测椭圆或其他曲线,更进一步,可以将该方法推广到其他任何任意形状。曲线的参数越多,使用霍夫变换检测曲线的空间和计算成本就越高。在本节中,我们将学习如何使用不同类型的霍夫变换来检测图像中不同形状的对象。
1. 使用圆形霍夫变换统计图像中圆形对象
在本节中,我们将学习如何使用圆形霍夫变换来统计图像中的圆形对象,并使用 scikit-image.transform
模块实现圆形对象统计。
(1) 首先导入所有必需的库函数:
import numpy as np
import matplotlib.pyplot as plt
from skimage.io import imread
from skimage.color import rgb2gray
from skimage.transform import hough_circle, hough_circle_peaks
from skimage.feature import canny
from skimage.draw import circle_perimeter
from skimage.util import img_as_ubyte
from sklearn.neighbors import KDTree
(2) 加载输入图像并使用 Canny
边缘检测器检测边缘:
orig = imread('1.png')
h, w = orig.shape[:2]
image = rgb2gray(orig)
edges = canny(image, sigma=1, low_threshold=0.15, high_threshold=0.45)
(3) 将函数 hog_circle()
应用于边缘图像,以搜索半径值在 10
到 20
像素之间的圆,并进行投票。选择得票最多的圆,我们将 total_num_peaks
参数设置为一个较高值。为了避免多次检测到同一单元,我们需要通过使用参数 min_xdistance
和 min_ydistance
确保检测到的两个相邻峰值之间的最小间隔。
hough_radii = np.arange(10, 20, 1)
hough_res = hough_circle(edges, hough_radii)accums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii,min_xdistance = 10,min_ydistance = 10,#num_peaks = 5,total_num_peaks=400)
(4) 使用 circle_perimeter()
函数绘制图像上检测到的圆,在参数空间和圆形霍夫变换上执行迭代。为了保证最小的间距,使用 KDTree
数据结构查询半径内的所有圆形:
circles = []
image = orig.copy()
for center_y, center_x, radius in zip(cy, cx, radii):circy, circx = circle_perimeter(center_y, center_x, radius, shape=image.shape)if len(circles) > 1:tree = KDTree(np.array(circles), leaf_size=2) count = tree.query_radius(np.array([[center_y, center_x]]), r=10, count_only=True)if count[0] > 0: continuecircles.append([center_y, center_x])for j in range(-3,4):image[np.minimum(circy+j,h-1), np.minimum(circx+j,w-1)] = (255, 0, 0)print(len(cx))
(5) 最后,绘制原始输入图像,用 Canny
检测到的边缘,以及使用霍夫变换检测到的圆:
plt.figure(figsize=(20, 8))
plt.gray()
plt.subplots_adjust(0,0,1,0.975,0.05,0.05)
plt.subplot(131), plt.imshow(orig), plt.axis('off'), plt.title('original', size=10)
plt.subplot(132), plt.imshow(edges), plt.axis('off'), plt.title('edges with canny', size=10)
plt.subplot(133), plt.imshow(image), plt.axis('off'), plt.title('circle detected', size=10)
plt.suptitle('Counting circles with Circle Hough transform, number of circles={}'.format(len(circles)), size=12)
plt.show()
2. 使用渐进概率霍夫变换检测直线
2.1 渐进霍夫变换原理
霍夫变换是一种流行的提取集合形状的常用方法,变换主要方面是参数化、累加器设计、投票模式和峰值检测。概率霍夫变换 (Probabilistic Hough Transform
, PHT
) 的目的是最大程度地减少投票中使用的点的比例,同时几乎可以达到标准霍夫变换的水平。
渐进概率霍夫变换 (Progressive Probabilistic Hough Transform
, PPHT
) 是自适应概率霍夫变换 (Adaptive Probabilistic Hough Transform
, APHT
) 的一种形式,其目的是通过利用可靠检测具有不同数量支持点的线(特征)所需的投票分数的差异,最大限度地减少检测线(或其他几何特征)所需要的计算量。
PPHT
反映了算法固有的直线检测过程的渐进性,该过程首先找到最长(最显着)的线,然后再检测较短的线。用于投票的分数不需要特别指定或使用先验知识,因为在概率霍夫变换中,它是输入数据固有的复杂函数。该算法非常适合对实时性要求较高的应用,因为投票和直线检测可以并行计算,最显著的特征很可能首先被检测到。实验表明,在许多情况下,PPHT
比标准 HT
更具优势。PPHT
算法描述如下:
- 循环选择新的随机点进行投票
- 投票后,检验计数是否可能是由于随机噪声引起
- 检验过程需要与每个bin更新的阈值进行一次比较
- 当检测到一条线时,支持点会撤回选票
- 支持该线的其余点将从尚未投票的点集中删除,然后进行下一次随机选择
PPHT
算法具有以下优势:
- 只需根据累加器决定是否检测到特征
- 算法允许被中断,仍然可以输出检测到的显著特征
- 该算法不需要停止迭代的条件,当所有点被投票或被分配给某个特征时,计算停止
在霍夫变换中,只有一小部分点可以投票,而其余部分作为检测到的特征的支持证据。例如,如果以最小线长度的形式给出约束,则可以在选择投票点之前测试停止条件。在本节中,我们将学习如何使用 transform
模块的 PPHT
实现,在图像中检测直线。
2.2 直线检测
(1) 首先导入所需的库和函数,读取输入图像,然后将其转换为灰度图像:
import matplotlib.pyplot as plt
from skimage.io import imread
from skimage.color import rgb2gray
from skimage.feature import canny
from skimage.color import rgb2gray
from skimage.transform import probabilistic_hough_line
(2) 调用函数 probabilitic_hough_line()
,其中:
line_length
参数指定的检测线的最小可接受长度line_gap
参数指定的形成直线的像素之间的最大间隙
image = rgb2gray(imread('1.png')) # the image have pixel values in the range [0,1]
edges = canny(image, 2, 30/255, 80/255)
lines = probabilistic_hough_line(edges, threshold=20, line_length=20, line_gap=5)
最后,绘制输入图像、边缘图像和输出图像:
fig, axes = plt.subplots(1, 3, figsize=(30, 20), sharex=True, sharey=True)
ax = axes.ravel()
plt.gray()
ax[0].imshow(image, cmap=plt.cm.gray)
ax[0].set_title('Input image', size=10)
ax[1].imshow(edges, cmap=plt.cm.gray)
ax[1].set_title('Canny edges', size=10)
ax[2].imshow(edges * 0)
for line in lines:p0, p1 = lineax[2].plot((p0[0], p1[0]), (p0[1], p1[1]), linewidth=5)
ax[2].set_xlim((0, image.shape[1]))
ax[2].set_ylim((image.shape[0], 0))
ax[2].set_title('Probabilistic Hough', size=10)
for a in ax:a.set_axis_off()
plt.axis('off')
plt.tight_layout()
plt.show()
3. 使用广义霍夫变换检测任意形状的对象
3.1 广义霍夫变换原理
广义霍夫变换 (Generalized Hough Transform
, GHT
) 是指使用模板匹配的原理对霍夫变换的变体;这种修改使霍夫变换可用于检测其模型所描述的任意对象。GHT
的原始实现使用边缘信息来定义从边缘点的方向到形状的参考点映射。参考点是其形状的局部坐标系的原点,GHT
测量参考点能否被认为是形状的局部坐标系的原点。
广义霍夫变换解释了如何使用任意非解析形状的边界来构建图像空间和霍夫变换空间之间的映射,可以利用这样的映射来检测图像中特定形状的实例。此外,形状的变化(例如旋转,比例变化或图形逆转)对应于该映射的直接转换。但是,最显着的特征是,可以组合这些映射,从简单形状和组件形状的映射中构建复杂形状的映射。这使得广义霍夫成为一种通用变换,可以用来寻找任意复杂的形状。
在本节中,我们将学习如何使用 OpenCV
的 createGeneralizedHoughard()
函数来检测任意形状(作为模板图像提供)。
3.2 检测自定义形状
(1) 我们首先导入所需的库和函数:
from matplotlib.pylab import imshow, title, show
from skimage.filters import threshold_otsu
import cv2
import numpy as np
import matplotlib,pylab as plt
(2) 读取输入和模板图像,将它们转换为灰度图像,并使用 Canny
进行边缘检测:
orig = cv2.imread('match_shapes.png')
img = 255-cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
templ = 255-cv2.imread('shape2.png', 0)
edges = cv2.Canny(img, 130,150)
(3) 使用 cv2
中的 createGeneralizedHoughBallard()
函数检测源图像内部的模板形状,并检索图像内部形状的位置以及对形状的相应投票:
alg = cv2.createGeneralizedHoughBallard()
alg.setTemplate(templ)
[positions,votes] = alg.detect(edges)
(4) 在检测到的包含形状的区域周围绘制边界框,在图像内部找到的可能位置的坐标表示形状的中心坐标:
clone = orig.copy() #np.dstack([edges, edges, edges])
for i in range(len(positions[0])):pos, scale, angle = positions[0][i][:2], positions[0][i][2], positions[0][i][3]print(pos, scale, angle)# need to write code here to rotate the bounding rect if angle is not zero and scale is not 1cv2.rectangle(clone, (int(pos[0]) - templ.shape[1]//2, int(pos[1]) - templ.shape[0]//2), (int(pos[0] + templ.shape[1]//2), int(pos[1] + templ.shape[0]//2)), (0,0,255), 2)
(5) 最后,绘制输入和模板图像以及边界框,在图像中可以看出,虽然模板图像与源图像中对象略有不同,但该算法仍可以正确找到图像内部的形状:
plt.figure(figsize=(20, 8))
plt.gray()
plt.subplots_adjust(0,0,1,0.975,0.05,0.05)
plt.subplot(131), plt.imshow(img), plt.axis('off'), plt.title('input', size=10)
plt.subplot(132), plt.imshow(templ), plt.axis('off'), plt.title('template', size=10)
plt.subplot(133), plt.imshow(clone), plt.axis('off'), plt.title('object detection with generalized Hough', size=10)
plt.show()
小结
霍夫变换是一种特征提取 (feature extraction
) 技术,在图像分析、计算机视觉等领域应用广泛,利用霍夫变换可以辨别并提取图像中的目标特征。本节中,我们学习了霍夫变换的基本原理,进一步将广义霍夫变换将其扩展到检测任意形状对象,并学习了如何利用霍夫变换检测图像中的目标对象。
系列链接
Python图像处理【1】图像与视频处理基础
Python图像处理【2】探索Python图像处理库
Python图像处理【3】Python图像处理库应用
Python图像处理【4】图像线性变换
Python图像处理【5】图像扭曲/逆扭曲
Python图像处理【6】通过哈希查找重复和类似的图像
Python图像处理【7】采样、卷积与离散傅里叶变换
Python图像处理【8】使用低通滤波器模糊图像
Python图像处理【9】使用高通滤波器执行边缘检测
Python图像处理【10】基于离散余弦变换的图像压缩
Python图像处理【11】利用反卷积执行图像去模糊
Python图像处理【12】基于小波变换执行图像去噪
Python图像处理【13】使用PIL执行图像降噪
Python图像处理【14】基于非线性滤波器的图像去噪
Python图像处理【15】基于非锐化掩码锐化图像
Python图像处理【16】OpenCV直方图均衡化
Python图像处理【17】指纹增强和细节提取
Python图像处理【18】边缘检测详解