likes
comments
collection
share

霍夫直线变换、空间及直线检测 + Python代码实现

作者站长头像
站长
· 阅读数 2

霍夫变换(Hough Transform)于1962年由Paul Hough 首次提出,后于1972年由Richard Duda和Peter Hart推广使用,是图像处理领域内从图像中检测几何形状的基本方法之一。经典霍夫变换用来检测图像中的直线。

霍夫变换运用两个坐标空间之间的变换,将在一个空间中具有相同形状的曲线或直线映射到另一个坐标空间的一个点上形成峰值,从而把检测任意形状的问题转化为统计峰值问题。

霍夫直线检测的基本原理

图像空间中的点与参数空间中的直线一一对应

霍夫直线变换、空间及直线检测 + Python代码实现

图像空间中的直线与参数空间中的点一一对应

两点所确定直线,相当于参数空间上两线间的交点

霍夫直线变换、空间及直线检测 + Python代码实现

但是,由于上面的变换不能表示斜率为无穷大的情况(垂直直线),因此,采用极坐标的方式:

霍夫直线变换、空间及直线检测 + Python代码实现

那么如此一来,多个点所形成直线将会在参数空间上呈现出一个交点

霍夫直线变换、空间及直线检测 + Python代码实现

伪代码

Initialize the accumulator (H) to all zeros
For each edge pixel (x,y) in the image
	For Θ = 0 to 180
		Calculate r (r = x*cosΘ + y*sinΘ)
		H(Θ,r) = H(Θ,r) +1
	endFor
endFor
Find the (Θ,r) value(s), where H(Θ,r) is above a suitable threshold value.

霍夫空间

使用样图:

霍夫直线变换、空间及直线检测 + Python代码实现

对应参数空间:

霍夫直线变换、空间及直线检测 + Python代码实现

霍夫空间展示代码

import numpy as np
import imageio
import math
import matplotlib.pyplot as plt


def rgb2gray(rgb):
    return np.dot(rgb[..., :3], [0.299, 0.587, 0.114]).astype(np.uint8)


def hough_line(img, angle_step=1, lines_are_white=True, value_threshold=5):
    # ρ 和 Theta 的范围
    thetas = np.deg2rad(np.arange(-90.0, 90.0, angle_step))
    width, height = img.shape
    diag_len = int(round(math.sqrt(width * width + height * height)))
    rhos = np.linspace(-diag_len, diag_len, diag_len * 2)

    # 缓存一些可重新使用的值
    cos_t = np.cos(thetas)
    sin_t = np.sin(thetas)
    num_thetas = len(thetas)

    # Hough 累加数组
    accumulator = np.zeros((2 * diag_len, num_thetas), dtype=np.uint8)
    # (row, col) 边的索引
    are_edges = img > value_threshold if lines_are_white else img < value_threshold
    y_idxs, x_idxs = np.nonzero(are_edges)

    # 在霍夫累加器投票
    for i in range(len(x_idxs)):
        x = x_idxs[i]
        y = y_idxs[i]

        for t_idx in range(num_thetas):
            # 计算ρ,为正索引添加diag_len
            rho = diag_len + int(round(x * cos_t[t_idx] + y * sin_t[t_idx]))
            accumulator[rho, t_idx] += 1

    return accumulator, thetas, rhos


def show_hough_line(img, accumulator, thetas, rhos, save_path=None):
    plt.imshow(accumulator, aspect='auto', cmap='jet',
               extent=[np.rad2deg(thetas[-1]), np.rad2deg(thetas[0]), rhos[-1], rhos[0]])
    if save_path is not None:
        plt.savefig(save_path, bbox_inches='tight')
    plt.show()


if __name__ == '__main__':
    imgpath = 'line.png'
    img = imageio.v2.imread(imgpath)
    if img.ndim == 3:
        img = rgb2gray(img)
    accumulator, thetas, rhos = hough_line(img)
    show_hough_line(img,
                    accumulator,
                    thetas, rhos,
                    save_path='output.png')

基于cv2的直线检测

import cv2
import numpy as np

# 利用霍夫变换算法检测图像中的直线
img = cv2.imread('line.png')
cv2.imshow('original', img)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

edges = cv2.Canny(gray, 50, 150, apertureSize=3)
cv2.imshow('Canny', edges)

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

# 逐条绘制直线
img1 = img.copy()
for line in lines:
    rho, theta = line[0]
    a = np.cos(theta)
    b = np.sin(theta)
    x0, y0 = a * rho, b * rho
    pt1 = (int(x0 + 1000 * (-b)), int(y0 + 1000 * (a)))  # 计算直线端点
    pt2 = (int(x0 - 1000 * (-b)), int(y0 - 1000 * (a)))  # 计算直线端点
    cv2.line(img1, pt1, pt2, (0, 0, 255), 2)  # 绘制直线

cv2.imshow('HoughLines', img1)
cv2.waitKey(0)
cv2.destroyAllWindows()

效果展示

霍夫直线变换、空间及直线检测 + Python代码实现