环境:Python3.8 和 OpenCV
内容:Hough直线检测
Hough直线检测将直角坐标系中的直线检测转换为极坐标系中的点检测
实现步骤:
1.读取灰度图I,并获取其边缘图像E
2.设置长度分辨率dr和角度分辨率dt,以及直线存在的点数阈值thresh
3.指定转换后的极坐标空间坐标轴的取值范围,分别记为[rmin, rmax],[tmin, tmax],将转换后的二维空间划分为多个bin,设置计数器N(r,θ),用于记录落入bin中的像素点
4.对边缘图像E的每个边缘点,其坐标记为(x,y),计算 r = xcosθ + ysinθ 对应的曲线在极坐标空间中所经过的点,所经过的点对应的N(r,θ) + 1
5.如果某个bin的点数超出阈值thresh,则认为存在对应的直线,取出该点(r,θ),求出直线方程。
实现方式1:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 封装图片显示函数
def image_show(image):
if image.ndim == 2:
plt.imshow(image, cmap='gray')
else:
image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()
if __name__ == '__main__':
# 读取灰度图
img_desk = cv.imread('desk.png')
# 转换为灰度图
img_gray = cv.cvtColor(img_desk, cv.COLOR_BGR2GRAY)
# 边缘检测
img_edge = cv.Canny(img_gray, 10, 300)
# 霍夫直线检测
lines = cv.HoughLines(img_edge, 2, np.pi / 180, 180)
# 循环绘制直线
length = img_gray.shape[0] + img_gray.shape[1]
for (rho, theta) in lines.squeeze():
# 计算直线方程的常数
const_a = np.cos(theta)
const_b = np.sin(theta)
# 获取直线的两点
x1 = int(rho * const_a - length * const_b)
y1 = int(rho * const_b + length * const_a)
x2 = int(rho * const_a + length * const_b)
y2 = int(rho * const_b - length * const_a)
# 绘制直线
cv.line(img_desk, (x1, y1), (x2, y2), (0, 0, 255), 2)
image_show(img_desk)
实现方式2:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 封装图片显示函数
def image_show(image):
if image.ndim == 2:
plt.imshow(image, cmap='gray')
else:
image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()
if __name__ == '__main__':
# 读取灰度图
img_desk = cv.imread('desk.png')
# 转换为灰度图
img_gray = cv.cvtColor(img_desk, cv.COLOR_BGR2GRAY)
# 边缘检测
img_edge = cv.Canny(img_gray, 10, 300)
# 霍夫直线检测
lines = cv.HoughLinesP(img_edge, 3, np.pi / 180, 140, minLineLength=30, maxLineGap=25)
# 循环绘制直线
for (x1, y1, x2, y2) in lines.squeeze():
cv.line(img_desk, (x1, y1), (x2, y2), (0, 0, 255), 2)
image_show(img_desk)
实现方式3:
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# 封装图片显示函数
def image_show(image):
if image.ndim == 2:
plt.imshow(image, cmap='gray')
else:
image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
plt.imshow(image)
plt.show()
if __name__ == '__main__':
# 读取灰度图
img_desk = cv.imread('desk.png')
# 转换为灰度图
img_gray = cv.cvtColor(img_desk, cv.COLOR_BGR2GRAY)
# 边缘检测
img_edge = cv.Canny(img_gray, 10, 300)
# 参数设置
dr = 2 # 长度分辨率
dt = np.pi / 180 # 角度分辨率
thresh = 180 # 点数阈值
# 设置极坐标空间的范围
rmin, rmax = 0, np.sqrt(img_edge.shape[0] ** 2 + img_edge.shape[1] ** 2)
tmin, tmax = 0, 2 * np.pi
# 设置网格个数
bin_r = int((rmax - rmin) / dr) + 1
bin_t = int((tmax - tmin) / dt) + 1
N = np.zeros((bin_r, bin_t), np.int32)
# 获取各边缘点的坐标
index_y, index_x = np.where(img_edge == 255)
# 循环计算直线,累加计数器
thetas = np.arange(tmin, tmax, dt)
for (x, y) in zip(index_x, index_y):
# 获取极坐标空间中各点位置(bin)
rhos = abs(x * np.cos(thetas) + y * np.sin(thetas))
ms = np.round(rhos / dr).astype(np.int32)
ns = np.round(thetas / dt).astype(np.int32)
# 累加计数器
for (rho, theta) in zip(ms, ns):
N[rho, theta] += 1
# 显示极坐标空间曲线结果
plt.imshow(N, cmap='jet')
plt.show()
# 获取超过阈值点数的位置
rs, ts = np.where(N > thresh)
rs = rs * dr
ts = ts * dt
# 循环绘制直线
length = img_gray.shape[0] + img_gray.shape[1]
for (rho, theta) in zip(rs, ts):
# 计算直线方程的常数
const_a = np.cos(theta)
const_b = np.sin(theta)
# 获取直线的两点
x1 = int(rho * const_a - length * const_b)
y1 = int(rho * const_b + length * const_a)
x2 = int(rho * const_a + length * const_b)
y2 = int(rho * const_b - length * const_a)
# 绘制直线
cv.line(img_desk, (x1, y1), (x2, y2), (0, 0, 255), 2)
image_show(img_desk)