开启生长之旅!这是我参与「日新方案 2 月更文应战」的第 28 天,点击查看活动概况

目标

在这一章傍边,将学习

  • 了解霍夫改换的概念
  • 运用它来检测图画中的线条
  • 函数:cv2.HoughLines()cv2.HoughLinesP()

理论

假如能够用数学方式表明形状,则霍夫改换是检测任何形状的一种比较盛行的技能。即使形状有些破损或变形,也能够检测出形状。本文将解说怎么将它何作用于一条线。

一条线能够表明为y=mx+cy = mx+c或以参数方式表明=xcos+ysin = xcos+ysin $,其间是从原点到该线的垂直间隔,而是由该垂直线和水平轴构成的视点以逆时针方向丈量(该方向随怎么表明坐标系而变化。此表明方式在OpenCV中运用)。如下图所示:

OpenCV 26: 霍夫直线变换
OpenCV 26: 霍夫直线变换

因而,假如线在原点下方通过,则它将具有正的 且视点小于180。假如线在原点上方,则将视点取为小于180,而不是大于180的视点。 取负值。任何垂直线将具有0度,水平线将具有90度

现在,看一下霍夫改换怎么处理线条。任何一条线都能够用(,)这两个术语表明。因而,首先创立2D数组或累加器(以保存两个参数的值),并将其初始设置为0。让行表明 ,列表明。阵列的大小取决于所需的精度。假定希望视点的精度为1度,则需求180列。关于最大间隔或许是图画的对角线长度。因而,以一个像素精度为准,行数能够是图画的对角线长度。

考虑一个100×100的图画,中间有一条水平线。取直线的第一点。此时知道它的(x,y)值。现在在线性方程式中,将值= 0,1,2,… 180放进去,然后查看得到。关于每对( , ),在累加器中对应的(, )单元格将值添加1。假定此点是(50,90),则该点的值加1,其它点依此类推。

现在,对行的第二个点。履行与上述相同的操作。递加( , ) 对应的单元格中的值。这次,单元格(50,90)=2。实践上,正在对( , )值进行投票。对线路上的每个点都继续履行此进程。在每个点上,单元格(50,90)都会添加或投票,而其他单元格或许会或或许不会投票。这样一来,**最终,单元格(50,90)的投票数将最高。**因而,假如在累加器中查找最大票数,则将获得(50,90)值,该值表明该图画中的一条线与原点的间隔为50,视点为90度

OpenCV 26: 霍夫直线变换

这就是霍夫改换对线条的工作方式,原理很简单。输入的图片中有两条粗直线,通过霍夫改换后的成果得到accumaltor矩阵,右图就是把accumaltor矩阵画出来,越亮值越大,越黑值越小。在右图中,有两个很明显的亮点, 这两个亮点分别代表两条不同参数的直线,与输入的图片(左图)符合。然后读取矩阵的两个最大值就能够得出这两条线距画面中心间隔以及视点。

OpenCV 26: 霍夫直线变换

OpenCV中的霍夫曼改换

上面说明的一切内容都封装在OpenCV函数cv2.HoughLines()中。

lines=cv2.HoughLines(image,rho,theta,threshold) 回来的lines:表明的是一个具有两个或三个元素的数组,math:(rho,theta), 其间 以像素为单位,弧度为单位 第一个参数,输入图画应该是二进制图画,因而在运用霍夫改换之前,请运用阈值或运用Canny边际检测 第二和第三参数分别是精度 第四个参数是阈值,这意味着应该将其视为行的最低投票。请记住,票数取决于线上的点数。因而,它表明应检测到的最小线长。

import cv2
import numpy as np
img = cv2.imread('sudo.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi/180, 100)  # 最终一个数是阈值控制,不同的值作用不一样
for line in lines:
    rho, theta = line[0]  # 最大的那一个
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    x1 = int(x0 + 1000*(-b))  # 以(x0, y0)为起点,将线段延伸
    y1 = int(y0 + 1000*(a))
    x2 = int(x0 - 1000*(-b))
    y2 = int(y0 - 1000*(a))
    cv2.line(img, (x1, y1), (x2, y2), (0,0,255), 2)
cv2.imshow('houglines', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV HoughLines 中,它是界说线条的端点,以便它们在绘制线条时到达(并通过)图画的侧面。您运用的霍夫改换仅回来线与原始线的视点和间隔。所以额外的核算是从原点垂直于这条线找到一条线的交点,这样它就能够识别这条线上的某个点。但它不知道这条线应该有多长。所以它沿着这条线从那个点延伸了这条线。因为它知道直线的视点和直线上的一个点,它只提供两个端点到直线上给定点的间隔。假如您的图画尺度大于约 21000 像素,那么假如您希望线条到达图画的两侧,则或许需求添加 1000 值。减号 (-b) 出现如下: 从原点到垂直于直线的方向由它的斜率给出b/a = sin(theta)/cos(theta)=tan(theta)。请参阅opencv-python-tutroals.readthedocs.io/en/latest/p… 上的图表。可是线自身的方向与该方向成 90 度,其视点由 给出-1/tan(theta) = -cos(theta)/sin(theta) = -a/b or a/-b。即它的斜率是 (a/-b)=(y-yo)/(x-xo)=delY/delX。请参阅byjus.com/maths/slope… xo, yo 给定的任意点开端,然后沿线向任一方向移动,这样端点 X 分量为 xo +- 1000 delX = xo +- 1000 cos(perp_angle) = xo +- 1000(-b) 终点 Y 分量是 yo +- 1000 delY = yo +- 1000sin(perp_angle) = yo +- 1000*a。其间 perp_angle 是沿实践线的方向。

分享

查看下面的成果

OpenCV 26: 霍夫直线变换

概率霍夫改换

在霍夫改换中,能够看到,即使关于带有两个参数的行,也需求大量核算。概率霍夫改换是霍夫改换的优化。它没有考虑一切关键。取而代之的是,它仅采用随机的点子集,足以进行直线检测。只是有必要降低阈值。

OpenCV的完成基于Matas,J.和Galambos,C.和Kittler, J.V.运用渐进概率霍夫改换对行进行的稳健检测 。运用的函数是cv2.HoughLinesP()。它有两个新的论点。

lines = cv.HoughLines( image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]] )

  • minLineLength – 最小行长。小于此长度的线段将被拒绝。假如有超越阈值个数的像素点构成了一条直线,可是这条直线很短,那么就不会承受该直线作为判别成果,而以为这条直线仅仅是图画中的若干个像素点刚好随机构成了一种算法上的直线关系罢了,实践上原图中并不存在这条直线。
  • maxLineGap – 线段之间答应将它们视为一条线的最大空隙。假如有超越阈值个数的像素点构成了一条直线,可是这组像素点之间的间隔都很远,就不会承受该直线作为判别成果,而以为这条直线仅仅是图画中的若干个像素点刚好随机构成了一种算法上的直线关系罢了,实践上原始图画中并不存在这条直线。

最好的是,它直接回来行的两个端点。在曾经的情况下,仅获得线的参数,并且有必要找到一切点。在这里,一切都是直接而简单的。

拜见下图,比较了霍夫空间中的霍夫改换和概率霍夫改换。

OpenCV 26: 霍夫直线变换

完成

# p 霍夫改换
import cv2
import numpy as np
img = cv2.imread('sudo.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength=100, maxLineGap=10)
for line in lines:
    x1, y1, x2, y2 = line[0]  # 这里直接回来的是坐标,不必改换
    cv2.line(img, (x1, y1), (x2, y2), (0, 255,0), 2)
cv2.imshow('houghlinep', img)
cv2.waitKey()
cv2.destroyAllWindows()

看到如下成果:

OpenCV 26: 霍夫直线变换

能够看到在这张图上,cv2.HoughLines作用比cv2.HoughLinesP要好,可是不一定一直好,比方下面一张

OpenCV 26: 霍夫直线变换

# compare
# p 霍夫改换
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('building.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 200, minLineLength=100, maxLineGap=10)
for line in lines:
   x1, y1, x2, y2 = line[0]
   cv2.line(img, (x1, y1), (x2, y2), (0, 255,0), 2)
img1= cv2.imread('building.png')
gray1= cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
edges1 = cv2.Canny(gray1, 50, 150, apertureSize=3)
lines1 = cv2.HoughLines(edges1, 1, np.pi/180, 200)
for line in lines1:
   rho, theta = line[0]
   a = np.cos(theta)
   b = np.sin(theta)
   x0 = a * rho
   y0 = b * rho
   x1 = int(x0 + 1000*(-b))
   y1 = int(y0 + 1000*(a))
   x2 = int(x0 - 1000*(-b))
   y2 = int(y0 - 1000*(a))
   cv2.line(img1, (x1, y1), (x2, y2), (0,0,255), 2)
plt.subplot(121)
plt.imshow(img1_rgb)
plt.title('HoughLines')
plt.xticks([])
plt.yticks([])
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img1_rgb = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
plt.subplot(122)
plt.imshow(img_rgb)
plt.title('HoughLinesP')
plt.xticks([])
plt.yticks([])
plt.show()

OpenCV 26: 霍夫直线变换

实践上,主要是HoughLines是一条直线而非线段,而那些小树叶又容易被误检测成图片,所以才会画的面貌。

附加资源

  • docs.opencv.org/4.1.2/d6/d1…

  • Hough Transform on Wikipedia

  • docs.opencv.org/4.1.2/dd/d1…

  • docs.opencv.org/4.1.2/dd/d1…

  • blog.csdn.net/ftimes/arti…

  • stackoverflow.com/questions/6…

  • www.cnblogs.com/kk17/p/9693…