简述
说到霍夫变换,做图像的知道经典霍夫变换最常用于检测规则曲线,如直线、圆、椭圆等。而广义霍夫变换是为了检出那些无法写出解析式的不规则形状,虽然在深度学习大行其道的时代,霍夫变换也还是有很多应用场景,另外广义霍夫变换本质上也是一种模板匹配算法。
很多时候模板匹配可以理解为,再大图中找小图,实际上广义霍夫变换也差不多是这个意思,只不过两个实现方式不是一样的。模板匹配是类似拿着小图在大图上滑动,寻找最契合就是匹配度最高的区域,比如OpenCV提供了几种计算距离的算法。而广义霍夫变换是根据膜版预先找到一组向量,然后遍历大图的所有边缘点,找所有跟这一组向量最接近的中心点。
模板匹配的原理可以看下面这个兄弟的博客,很简明。
opencv 模板匹配_浅谈opencv模板匹配算法原理_weixin_39707201的博客-CSDN博客
广义霍夫变换
第 1 步:预处理模板和待匹配两个图像(查找边缘及其方向,x,y方向的导数),实际就是模板提取边缘并二值化。
第 2 步:模板形状规范(计算 r 表),实际就是针对模板图像设立一个参考点, 并统计各个边缘点相对参考点构成的向量, 存储到R-table中,边缘点梯度方向的参考点。
步骤3:查找(累积)图像上模板的所有可能位置并找到最常用的中心。对照模板图像的R-table, 我们对待匹配图像上的每一个边缘点,去减每个R-table中的向量坐标并投1票。
广义霍夫变换可用于对象识别。一个优点是该算法可用于任意形状,因为我们只需要参数化形状的轮廓。
示例参考
请看下面的参考图片,下面的图片是模板图,实际上下面左图的红点就是参考点,计算了边缘的点相对于中心点的距离和角度,并存储到R-table中。这个表中的每个条目表示一个边缘像素位置关于中心点的(r,α)的值。有了这个表,我们就可以在待匹配图像上遍历所有边缘点进行计算了。
广义霍夫变换可用于对象识别。一个优点是该算法可用于任意形状,因为我们只需要参数化形状的轮廓。缺点是计算累加器时需要大量存储和大量计算。
参考代码
以下代码来自下面的参考文章
边缘检测
def find_edge_orientation(image):
dy = sobel(image, axis=0)
dx = sobel(image, axis=1)
edge_orientation = np.arctan2(dy, dx) + np.pi # +np.pi -> grad orientation is in the rage [0,2*pi]
return edge_orientation
计算R表
def build_r_table(template, center_point, row_count):
edges = canny(template)
orientations = find_edge_orientation(edges)
r_table = [[] for i in range(row_count)]
for (i, j), value in np.ndenumerate(edges):
if value:
r = np.sqrt((center_point[0] - i) ** 2 + (center_point[1] - j) ** 2) # compute r
alpha = np.arctan2(i - center_point[0], j - center_point[1]) + np.pi # compute alpha
index = int(row_count * orientations[i, j] / (2 * np.pi)) # compute row index in the table
r_table[index].append((r, alpha)) # append the value to R-table
return r_table
查找可能的位置
def compute_accumulator_array(target_I, r_table):
edges = canny(target_I)
orientations = find_edge_orientation(edges)
accumulator = np.zeros(target_I.shape)
r_table_row_count = len(r_table)
vals = []
for (i, j), value in np.ndenumerate(edges):
if value: # edge check
index = int(r_table_row_count * orientations[i, j] / (2 * np.pi))
r_row = r_table[index]
for (r,alpha) in r_row:
# finding correct position to vote
accum_i = int(i + r * np.sin(alpha)) # y cordinate
accum_j = int(j + r * np.cos(alpha)) # x cordinate
# outofbound check and vote
if accum_i < accumulator.shape[0] and accum_j < accumulator.shape[1] and accum_i > 0 and accum_j > 0:
accumulator[accum_i, accum_j] += 1
return accumulator
相关参考网址
广义霍夫变换 - 知乎 (zhihu.com)
https://penny-xu.github.io/blog/generalised-hough-transform/