python学opencv|读取图像(六十九)使用cv2.HoughLinesP()函数实现图像中的霍夫直线检测

【1】引言

前序学习进程中,已经对边缘检测有了一定的了解,相关文章链接为:

python学opencv|读取图像(六十四)使用cv2.findContours()函数+cv2.drawContours()函数实现图像轮廓识别和标注-CSDN博客

python学opencv|读取图像(六十八)使用cv2.Canny()函数实现图像边缘检测-CSDN博客

在此基础上,如果想绘制图像边缘轮廓中的直线,就会用到霍夫直线变换。实质上,霍夫直线变换这算是一种特征检测,它检测到直线,然后才能画直线。

霍夫直线变换调用的函数为cv2.HoughLinesP()。

【2】官网教程

点击下方链接,直达霍夫直线变换调用的函数cv2.HoughLinesP()的官网教程:

OpenCV: Feature Detection

官网页面对cv2.HoughLinesP()函数的说明为:

图1  官网页面对cv2.HoughLinesP()函数的说明

具体的,官网页面对cv2.HoughLinesP()函数的参数说明为:

cv.HoughLinesP(    

image                              #输入图像,8位单通道灰度图

rho                                   #检测长度用的步长,取值=1时检测所有可能的长度

theta                                #检测角度用的步长,取值=np.pi/180时检测所有可能的角度

threshold                         #检测阈值,越小时检测的直线越多

lines                                #输出值,输出直线的两个端点坐标

minLineLength                #线段最小长度,小于该长度的线段不输出

maxLineGap)                  #线段之间的最小距离

【3】代码测试

首先是引入必要的模块和相关的图像:

import cv2 as cv # 引入CV模块
import numpy as np #引入numpy模块

# 读取图片
src = cv.imread('srctt.png') #读取图像

然后对图像进行预处理 :

#预处理图像
srcb=cv.medianBlur(src,3) #中值滤波
gray=cv.cvtColor(srcb,cv.COLOR_BGR2GRAY) #将图像转化为灰度图
r=cv.Canny(gray,100,280) #边缘检测

图像预处理的过程是层层递进的:

首先进行中值滤波处理,可以实现图像降噪;

然后将降噪后的图像转化为灰度图;

之后对灰度图进行边缘检测。

边缘检测的目的是,养这些边缘检测的值来做霍夫直线变换,由此获得紧贴轮廓的直线

图2 图像处理流程

之后进行霍夫直线变换:

#霍夫直线变换
lines1=cv.HoughLinesP(r,1,np.pi/180,60,minLineLength=80,maxLineGap=10)

然后显示和保存图像:

#绘制霍夫直线变换获得的直线
for line in lines1:
    x1,y1,x2,y2=line[0]
    cv.line(src,(x1,y1),(x2,y2),(200,150,200),5)

#显示和保存图像
cv.imshow('gr', gray)
cv.imshow('r',r)
cv.imshow('src',src)
cv.imwrite('ll.png', src) #保存图像

cv.waitKey()  # 图像不关闭
cv.destroyAllWindows()  # 释放所有窗口

代码运行相关的图像有:

图3 初始图像

图4 霍夫变换直线检测后的图像 

由图4可见,经过霍夫直线变换检测,部分直线被检测到,并被添加到图像上。

【4】代码优化

实际上,前述代码只描绘了一种情况的霍夫直线变换检测结果,如果改变cv2.HoughLinesP()函数的参数,想多检测几次,就需要扩充代码,比如:

#霍夫直线变换
lines1=cv.HoughLinesP(r,1,np.pi/180,60,minLineLength=80,maxLineGap=10)
lines2=cv.HoughLinesP(r,1,np.pi/180,80,minLineLength=80,maxLineGap=10)
lines3=cv.HoughLinesP(r,1,np.pi/180,80,minLineLength=100,maxLineGap=10)
lines4=cv.HoughLinesP(r,1,np.pi/180,80,minLineLength=100,maxLineGap=30)
lines5=cv.HoughLinesP(r,1,np.pi/30,80,minLineLength=100,maxLineGap=30)

#绘制霍夫直线变换获得的直线
for line in lines1:
    x1, y1, x2, y2 = line[0]
    l1 = cv.line(src, (x1, y1), (x2, y2), (200, 150, 200), 5)
    cv.imshow('l1', l1)
    cv.imwrite('l1.png', l1)  # 保存图像

for line in lines2:
    x1, y1, x2, y2 = line[0]
    l2 = cv.line(src, (x1, y1), (x2, y2), (200, 150, 200), 5)
    cv.imshow('l2', l2)
    cv.imwrite('l2.png', l2)  # 保存图像

for line in lines3:
    x1, y1, x2, y2 = line[0]
    l3 = cv.line(src, (x1, y1), (x2, y2), (200, 150, 200), 5)
    cv.imshow('l3', l3)
    cv.imwrite('l3.png', l3)  # 保存图像

for line in lines4:
    x1, y1, x2, y2 = line[0]
    l4 = cv.line(src, (x1, y1), (x2, y2), (200, 150, 200), 5)
    cv.imshow('l4', l4)
    cv.imwrite('l4.png', l4)  # 保存图像

for line in lines5:
    x1, y1, x2, y2 = line[0]
    l5 = cv.line(src, (x1, y1), (x2, y2), (200, 150, 200), 5)
    cv.imshow('l5', l5)
    cv.imwrite('l5.png', l5)  # 保存图像

实际上,这样做的目标就是逐个改变cv2.HoughLinesP()函数的参数,这个过程可以用一个for循环完成:

#霍夫直线变换
params_list = [
    (1, np.pi/180, 60, 80, 10),
    (1, np.pi/180, 80, 80, 10),
    (1, np.pi/180, 80, 100, 10),
    (1, np.pi/180, 80, 100, 30),
    (1, np.pi/30, 80, 100, 30)
]

# 假设 r 和 src 已经定义
transformed_images = [src]  # 先将原始图像添加到列表中

# 假设 r 和 src 已经定义
for i, (rho, theta, threshold, minLineLength, maxLineGap) in enumerate(params_list, start=1):
    # 霍夫直线变换
    lines = cv.HoughLinesP(r, rho, theta, threshold, minLineLength=minLineLength, maxLineGap=maxLineGap)
    if lines is not None:
        img = src.copy()  # 复制原图像,避免多次绘制影响
        for line in lines:
            x1, y1, x2, y2 = line[0]
            img = cv.line(img, (x1, y1), (x2, y2), (200, 150, 200), 5)
        transformed_images.append(img)

 这里首先用params_list矩阵,收集了所有的cv2.HoughLinesP()函数参数,然后用一个for循环,通过使用enumerate()函数将所有参数按照顺序代入cv2.HoughLinesP()函数进行运算。

enumerate(params_list, start=1)函数是一个枚举器,代表从第一个params_list的子项开始,逐个调用子项中的(rho, theta, threshold, minLineLength, maxLineGap)参数。for后的i其实就是个计数器,代表第几次调用参数,一般情况下,i的起始数据是0,第一次调用会被系统记录是第0次调用,而因为start=1约定了i的起始=1,所以第一次调用会被系统认为是第1次调用。、

然后仔细观察代码,会发现有一行代码使用了src:

transformed_images = [src]  # 先将原始图像添加到列表中

这行代码的目的是,想把经过霍夫直线变换的图像和初始图像放在一起比较,先让一个矩阵存储一下初始图像src。

在for循环中,同样还有一行代码:

        transformed_images.append(img)

这行代码的作用是,在每一次执行for循环后,会把获得的霍夫直线变换的图像直接添加在src后面。

然后因为一共做了5次霍夫直线变换,加上初始图像,一共6个小图,所以可以组合成一个2X3的大图,这时候为了谨慎,先检测一下是否有6个图:

# 确保图像数量足够进行两行三列的拼接
while len(transformed_images) < 6:
    # 如果图像数量不足 6 个,用空白图像填充
    blank_image = np.zeros_like(src)
    transformed_images.append(blank_image)

这个while循环中的blank_image = np.zeros_like(src)的意思是,生成一个和src等大的纯0矩阵,这是个纯黑色图。

之后按照顺序组合成两行三列的大图:

# 分割图像列表为两行
first_row = transformed_images[:3]
second_row = transformed_images[3:6]

# 水平拼接每行的图像
h_concat_first_row = cv.hconcat(first_row)
h_concat_second_row = cv.hconcat(second_row)

# 垂直拼接两行的图像
final_image = cv.vconcat([h_concat_first_row, h_concat_second_row])

最后显示和保存图像:

# 显示和保存最终拼接图像
cv.imshow('Concatenated Images', final_image)
cv.imwrite('concatenated_images.png', final_image)

# 等待按键关闭窗口
cv.waitKey()
cv.destroyAllWindows()

代码运行后,获得的图像为:

图5  多个霍夫直线变换图像对比

图5显示了多个霍夫直线变换图像对比效果,相对来说,第一行第三列的图像效果好一些。

第一行第三列的图像和第一行第二列的图像相比,调高了阈值,从60到80;

第二行第一列的图像和第一行第三列的图像相比。提高了线段最小长度,从80到100,所以部分短的直线没有输出;

第二行第二列的图像和第二行第一列的图像相比。提高了线段之间的最小距离,从10到30,输出了更多线段;

第二行第三列的图像和第二行第二列的图像相比。增大了检测角度用的步长,从np.pi/180到np.pi/30,输出了更少的线段。

【5】总结

掌握了python+opencv实现调用cv2.HoughLinesP()函数实现图像中的霍夫变换直线检测。

猜你喜欢

转载自blog.csdn.net/weixin_44855046/article/details/145628004