霍夫变换直线检测原理和应用

1. 引言

今天我们将重点讨论霍夫变换,这是一种非常经典的线检测的算法,通过将图像中的点映射到参数空间中的线来实现。霍夫变换可以检测任何方向的线,并且可以在具有大量噪声的图像中很好地工作。
闲话少说,我们直接开始吧!

2. 基础知识

为了理解霍夫变换的工作原理,首先我们需要了解直线是如何在极坐标系中定义的。直线由ρ(距原点的垂直距离)和θ(垂直线与轴线的夹角)来描述,如下图所:
在这里插入图片描述
因此,该直线的方程式为:
在这里插入图片描述
我们可以将其转化下表述形式,得到如下公式:
在这里插入图片描述
从上面的方程中,我们可以看出,所有具有相同ρ和θ值的点构成一条直线。我们算法的基础是针对θ的所有可能值计算图像中每个点的ρ值。

3. 算法原理

霍夫变换的处理步骤如下:
1)首先我们创建一个参数空间(又叫做霍夫空间)。参数空间是ρ和θ的二维矩阵,其中θ的范围在0–180之间。
2)使用诸如Canny边缘之类的边缘检测算法检测图像的边缘之后运行该算法。值为255的像素被认为是边缘。
3)接着我们逐像素扫描图像以找到这些边缘像素,并通过使用从0到180的θ值来计算每个像素的ρ。对于同一直线上的像素,θ和rho的值将是相同的。我们在霍夫空间中以1的权重对其投票。
4)最后,投票超过一定阈值的ρ和θ的值被视为直线。

代码处理过程如下:

def hough(img):
    # Create a parameter space
    # Here we use a dictionary
    H=dict()
    # We check for pixels in image which have value more than 0(not black)
    co=np.where(img>0)
    co=np.array(co).T
    for point in co:
        for t in range(180):
            # Compute rho for theta 0-180
            d=point[0]*np.sin(np.deg2rad(t))+point[1]*np.cos(np.deg2rad(t))
            d=int(d)
            # Compare with the extreme cases for image
            if d<int(np.ceil(np.sqrt(np.square(img.shape[0]) + np.square(img.shape[1])))):
                if (d,t) in H:
                    # Upvote
                    H[(d,t)] += 1
                else:
					# Create a new vote
                    H[(d,t)] = 1
    return H

4. 算法应用

在本文中,我们将检测图像中对象(书籍)的角点。这似乎是一项简单的任务,然而,它将让我们深入了解使用霍夫变换检测直线的过程。

4.1 彩色图到HSV空间

由于直接RGB图像做这项任务略有难度,我们不妨将该图像转换为HSV颜色空间,以便在HSV范围内轻松获取我们的目标。
核心代码如下:

img = cv2.imread("book.jpeg")
scale_percent = 30 # percent of original size
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
# resize image
img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

得到结果如下所示:
在这里插入图片描述

4.2 高斯模糊

我们应用高斯模糊来平滑图像中由于噪声而产生的粗糙边缘,进而可以突出我们图像中的目标,代码如下:

# Apply gaussian blur to he mask
blur = cv2.GaussianBlur(hsv, (9, 9), 3)

结果如下所示:
在这里插入图片描述

4.3 二值化和腐蚀操作

接着我们使用inRange函数来得到二值化图像。这使我们能够摆脱图像中其他周围的物体。代码如下:

# Define the color range for the ball (in HSV format)
lower_color = np.array([0, 0, 24],np.uint8)
upper_color = np.array([179, 255, 73],np.uint8)
# Define the kernel size for the morphological operations
kernel_size = 7
# Create a mask for the ball color using cv2.inRange()
mask = cv2.inRange(blur, lower_color, upper_color)

得到结果如下:
在这里插入图片描述
我们观察上图,存在或多或少的缝隙,我们不妨使用腐蚀操作来填补这些缝隙。代码如下:

# Apply morphological operations to the mask to fill in gaps
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size))
mask = cv2.dilate(mask, kernel,iterations=1)

结果如下:
在这里插入图片描述

4.4 边缘检测

边缘检测canny主要用于检测边缘。这主要是因为目标和周围背景之间的高对比度。代码如下:

#  Use canny edges to get the edges of the image mask
edges = cv2.Canny(mask,200, 240, apertureSize=3)

结果如下图所示:
在这里插入图片描述

4.5 霍夫变换

当我们进行canny边缘检测时,我们得到了很多边缘。因此,当我们运行霍夫算法时,这些边为同一条边贡献了许多条候选线。为了解决这个问题,我们对霍夫空间中ρ和θ的相邻值进行聚类,并对它们的值进行平均,得到它们的上投票数之和。这导致了描绘相同边缘的线条的合并,代码如下:

# Get the hough space, sort and select to 20 values
hough_space = dict(sorted(hough(edges).items(), key=lambda item: item[1],reverse=True)[:20])
# Sort the hough space w.r.t rho and theta
sorted_hough_space_unfiltered = dict(sorted(hough_space.items()))
# Get the unique rhoand theta values
unique_=unique(sorted_hough_space_unfiltered)
# Sort according to value and get the top 4 lines
unique_=dict(sorted(unique_.items(), key=lambda item: item[1],reverse=True)[:4])

得到结果如下:
在这里插入图片描述

4.6 计算角点

根据在霍夫空间中获得的直线,我们可以使用线性代数对其进行角点求解。这可以求出我们两条直线的交叉点,也就是书的角点,代码如下:

# Create combinations of lines
line_combinations = list(combinations(unique_.items(), 2))

intersection=[]
filter_int=[]
for (key1, value1), (key2, value2) in line_combinations:
    try:
	# Solve point of intersection of two lines
        intersection.append(intersection_point(key1[0],np.deg2rad(key1[1]), key2[0],np.deg2rad(key2[1]))) 
    except:
        print("Singular Matrix")

for x,y in intersection:
    if x>0 and y>0:
		# Get the valid cartesan co ordinates
        cv2.circle(img, (x, y), 5, (0, 0, 0), -1)
        cv2.putText(img, '{},{}'.format(x,y), (x-10, y), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
        filter_int.append([x,y])

最终输出如下图所示:
在这里插入图片描述

5. 总结

尽管该算法现已集成在各种各样的图像处理库,但本文通过自己实现它,我们可以深入了解在创建如此复杂的算法时所面临的挑战和局限性。

嗯嗯,您学废了嘛?

代码链接:戳我

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
import cv2 as cv import numpy as np #直线检测 #使用霍夫直线变换做直线检测,前提条件:边缘检测已经完成 #标准霍夫线变换 def line_detection(image): gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) edges = cv.Canny(gray, 50, 150) #apertureSize参数默认其实就是3 cv.imshow("edges", edges) #cv.HoughLines参数设置:参数1,灰度图像;参数二,以像素为单位的距离精度(一般都是1,进度高,但是速度会慢一点) #参数三,以弧度为单位的角度精度(一般是1rad);参数四,阈值,大于阈值threshold的线段才可以被检测通过并返回到结果中 #该函数返回值为rho与theta lines = cv.HoughLines(edges, 1, np.pi/180, 200) for line in lines: rho, theta = line[0] #line[0]存储的是点到直线的极径和极角,其中极角是弧度表示的。 a = np.cos(theta) #theta是弧度 b = np.sin(theta) x0 = a * rho #代表x = r * cos(theta) y0 = b * rho #代表y = r * sin(theta) x1 = int(x0 + 1000 * (-b)) #计算直线起点横坐标 y1 = int(y0 + 1000 * a) #计算起始起点纵坐标 x2 = int(x0 - 1000 * (-b)) #计算直线终点横坐标 y2 = int(y0 - 1000 * a) #计算直线终点纵坐标 注:这里的数值1000给出了画出的线段长度范围大小,数值越小,画出的线段越短,数值越大,画出的线段越长 cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) #点的坐标必须是元组,不能是列表。 cv.imshow("image-lines", image) #统计概率霍夫线变换 def line_detect_possible_demo(image): gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) edges = cv.Canny(gray, 50, 150, apertureSize=3) # apertureSize参数默认其实就是3 lines = cv.HoughLinesP(edges, 1, np.pi / 180, 60, minLineLength=60, maxLineGap=5) for line in lines: x1, y1, x2, y2 = line[0] cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) cv.imshow("line_detect_possible_demo",image) src = cv.imread("E:/opencv/picture/track.jpg") print(src.shape) cv.namedWindow('input_image', cv.WINDOW_AUTOSIZE) cv.imshow('input_image', src) line_detection(src) src = cv.imread("E:/opencv/picture/track.jpg") #调用上一个函数后,会把传入的src数组改变,所以调用下一个函数时,要重新读取图片 line_detect_possible_demo(src) cv.waitKey(0) cv.destroyAllWindows() 霍夫检测直线原理: 关于hough变换,核心以及难点就是关于就是有原始空间到参数空间的变换上。以直线检测为例,假设有一条直线L,原点到该直线的垂直距离为p,垂线与x轴夹角为θθ,那么这条直线是唯一的,且直线的方程为 ρ=xcosθ+ysinθρ=xcosθ+ysinθ, 如下图所
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵卓不凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值