0 原理

  霍夫变换在检测各种形状的的技术中非常流行,如果你要检测的形状可以用数学表达式写出,你就可以是使用霍夫变换检测它。及时要检测的形状存在一点破坏或者扭曲也可以使用。我们下面就看看如何使用霍夫变换检测直线。

首先将一条直线用一个点表示,这样用一个点表示直线上所有的点,一开始人们使用斜截式y=kx+q中的(k,q)来表示一条直线。

  首先将一条直线用一个点表示,这样用一个点表示直线上所有的点,一开始人们使用斜截式y=kx+q中的(k,q)来表示一条直线。

变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点。

反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):

再来看看A、B两个点,对应霍夫空间的情形:

一步步来,再看一下三个点共线的情况:

可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。

如果不止一条直线呢?再看看多个点的情况(有两条直线):

其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式:选择由尽可能多直线汇成的点。

看看,霍夫空间:选择由三条交汇直线确定的点(中间图),对应的笛卡尔坐标系的直线(右图)。

到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?

但这样做会有一个弊端,那就是当直线趋近于垂直时,斜率a会趋近于无穷大。这一困难的解决方法之一就是使用法线来表示直线。

在极坐标系下,其实是一样的:极坐标的点→霍夫空间的正弦线,这条正弦线上的所有点都过极坐标中的那个点。

简单来说就是每个点在霍夫空间的弦线与其他点的弦线交一次,就会在累加器的ρθ平面中累加一次。下面的动画很好的演示了这个过程

1 OpenCV中的霍夫变换

上面介绍的整个过程在OpenCV中都被封装进了一个函数:cv2.HoughLines()。返回值就是极坐标表示的直线(ρ, θ)。ρ 的单位是像素,θ 的单位是弧度。

cv2.HoughLines(image, rho, theta, threshold, lines, sen, stn, min_theta, max_theta)

  • image:输入图像,8-bit灰度图像
  • rho:生成极坐标时候的像素扫描步长
  • theta:生成极坐标时候的角度步长
  • threshold:阈值,只有获得足够交点的极坐标点才被看成是直线
  • lines:返回值,极坐标表示的直线(ρ, θ)
  • sen:是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
  • stn:是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
  • min_theta:表示角度扫描范围最小值
  • max_theta:表示角度扫描范围最大值

这种方法仅仅是一条直线都需要两个参数,这需要大量的计算。Probabilistic_Hough_Transform 是对霍夫变换的一种优化。它 不会对每一个点都进行计算,而是从一幅图像中随机选取(是不是也可以使用 图像金字塔呢?)一个点集进行计算,对于直线检测来说这已经足够了。但是 使用这种变换我们必须要降低阈值(总的点数都少了,阈值肯定也要小呀!)。

函数如下:

cv2.HoughLinesP(image, rho, theta, threshold, lines, minLineLength, maxLineGap)

  • src:输入图像,必须8-bit的灰度图像
  • rho:生成极坐标时候的像素扫描步长
  • theta:生成极坐标时候的角度步长
  • threshold:阈值,只有获得足够交点的极坐标点才被看成是直线
  • lines:输出的极坐标来表示直线
  • minLineLength:最小直线长度,比这个短的线都会被忽略。
  • maxLineGap:最大间隔,如果小于此值,这两条直线 就被看成是一条直线。

举个例子:

import cv2
import numpy as np img = cv2.imread('test19.jpg')
img1 = img.copy()
img2 = img.copy()
img = cv2.GaussianBlur(img, (3, 3), 0)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi/180, 110) for line in lines:
rho = line[0][0]
theta = line[0][1]
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) lines = cv2.HoughLinesP(edges, 1, np.pi/180, 30, 300, 5) for line in lines:
x1 = line[0][0]
y1 = line[0][1]
x2 = line[0][2]
y2 = line[0][3]
cv2.line(img2, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow('houghlines3', img1)
cv2.imshow('edges', img2)
cv2.waitKey(0)
print(lines)

结果如下:

带有滑动条,用于调节HoughLines的参数

import cv2
import numpy as np
from matplotlib import pyplot as plt def nothing(x): # 滑动条的回调函数
pass src = cv2.imread('test19.jpg')
srcBlur = cv2.GaussianBlur(src, (3, 3), 0)
gray = cv2.cvtColor(srcBlur, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
WindowName = 'Approx' # 窗口名
cv2.namedWindow(WindowName, cv2.WINDOW_AUTOSIZE) # 建立空窗口 cv2.createTrackbar('threshold', WindowName, 0, 60, nothing) # 创建滑动条 while(1):
img = src.copy()
threshold = 100 + 2 * cv2.getTrackbarPos('threshold', WindowName) # 获取滑动条值 lines = cv2.HoughLines(edges, 1, np.pi/180, threshold) for line in lines:
rho = line[0][0]
theta = line[0][1]
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(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imshow(WindowName, img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()

结果如下:

带有滑动条,用于调节HoughLinesP的参数

import cv2
import numpy as np
from matplotlib import pyplot as plt def nothing(x): # 滑动条的回调函数
pass src = cv2.imread('test19.jpg')
srcBlur = cv2.GaussianBlur(src, (3, 3), 0)
gray = cv2.cvtColor(srcBlur, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
WindowName = 'Approx' # 窗口名
cv2.namedWindow(WindowName, cv2.WINDOW_AUTOSIZE) # 建立空窗口 cv2.createTrackbar('threshold', WindowName, 0, 100, nothing) # 创建滑动条
cv2.createTrackbar('minLineLength', WindowName, 0, 100, nothing) # 创建滑动条
cv2.createTrackbar('maxLineGap', WindowName, 0, 100, nothing) # 创建滑动条 while(1):
img = src.copy()
threshold = cv2.getTrackbarPos('threshold', WindowName) # 获取滑动条值
minLineLength = 2 * cv2.getTrackbarPos('minLineLength', WindowName) # 获取滑动条值
maxLineGap = cv2.getTrackbarPos('maxLineGap', WindowName) # 获取滑动条值 lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold, minLineLength, maxLineGap) for line in lines:
x1 = line[0][0]
y1 = line[0][1]
x2 = line[0][2]
y2 = line[0][3]
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow(WindowName, img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()

结果如下:

霍夫(Hough)直线变换(直线检测)的更多相关文章

  1. opencv —— HoughCircles 霍夫圆变换原理及圆检测

    霍夫圆变换原理 霍夫圆变换的基本原理与霍夫线变换(https://www.cnblogs.com/bjxqmy/p/12331656.html)大体类似. 对直线来说,一条直线能由极径极角(r,θ)表 ...

  2. OpenCV 学习笔记03 直线和圆检测

    检测边缘和轮廓不仅重要,还经常用到,它们也是构成其他复杂操作的基础. 直线和形状检测与边缘和轮廓检测有密切的关系. 霍夫hough 变换是直线和形状检测背后的理论基础.霍夫变化是基于极坐标和向量开展的 ...

  3. opencv —— HoughLines、HoughLinesP 霍夫线变换原理(标准霍夫线变换、多尺度霍夫线变换、累积概率霍夫线变换)及直线检测

    霍夫线变换的原理 一条直线在图像二维空间可由两个变量表示,有以下两种情况: ① 在笛卡尔坐标系中:可由参数斜率和截距(k,b)表示. ② 在极坐标系中:可由参数极经和极角(r,θ)表示. 对于霍夫线变 ...

  4. Android开发中的OpenCV霍夫直线检测(Imgproc.HoughLines()&Imgproc.HoughLinesP())

    本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃   //2017-04-21更新: 很多网友希望能得到源码,由于在公司做的,所以不太方便传出来 ...

  5. 霍夫直线检测 opencv

    本次实验是检测图像中的直线,用到了HoughLines()和HoughLinesP()函数,其中HoughLinesP()称为累计概率霍夫变换,实验结果显示累计概率霍夫变换要比标准霍夫变换的效果好.具 ...

  6. opencv:霍夫直线检测

    #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...

  7. opencv-霍夫直线变换与圆变换

    转自:https://blog.csdn.net/poem_qianmo/article/details/26977557 一.引言 在图像处理和计算机视觉领域中,如何从当前的图像中提取所需要的特征信 ...

  8. Matlab 霍夫变换 ( Hough Transform) 直线检测

    PS:好久没更新,因为期末到了,拼命复习中.复习久了觉得枯燥,玩玩儿霍夫变换直线检测 霍夫变换的基本原理不难,即便是初中生也很容易理解(至少在直线检测上是这样子的). 霍夫变换直线检测的基本原理:(不 ...

  9. Win8 Metro(C#)数字图像处理--2.38Hough变换直线检测

    原文:Win8 Metro(C#)数字图像处理--2.38Hough变换直线检测  [函数名称] Hough 变换直线检测         HoughLineDetect(WriteableBit ...

  10. OpenCV2马拉松第22圈——Hough变换直线检測原理与实现

    计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/27220445 收入囊中 Hough变换 概率Ho ...

随机推荐

  1. tensorflow的官方强化学习库agents的相关内容及一些注意事项

    源代码地址: https://github.com/tensorflow/agents TensorFlow给出的官方文档说明: https://tensorflow.google.cn/agents ...

  2. 多线程之深入理解park与unpark

    1.背景 面试官问,如何暂停一个线程勒..... 说说你对park的理解....... 2.代码 package com.ldp.demo01; import com.common.MyThreadU ...

  3. 聊一聊 Netty 数据搬运工 ByteBuf 体系的设计与实现

    本文基于 Netty 4.1.56.Final 版本进行讨论 时光芿苒,岁月如梭,好久没有给大家更新 Netty 相关的文章了,在断更 Netty 的这段日子里,笔者一直在持续更新 Linux 内存管 ...

  4. 代码随想录Day16

    513.找树左下角的值 给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值. 假设二叉树中至少有一个节点. 示例 1: 输入: root = [2,1,3] 输出: 1 示 ...

  5. inline,static inline

    https://blog.csdn.net/A_BCDEF_/article/details/89485894 inline 函数被调用时,需要出栈入栈.当函数频繁被调用时,则不断地有函数出栈入栈,会 ...

  6. Linux命令lsscsi详解

    @ 目录 一.什么是scsi 二.常见硬盘接口 1. IDE接口的硬盘 2. SCSI接口硬盘 3. SATA接口类型 三. lsscsi命令 1. lsscsi 命令安装 2. lsscsi命令英文 ...

  7. pipx 使用

    pipx 用于在孤立环境中安装和运行 Python 应用程序.类似 Node.js 中的 npx. 安装 macOS: brew install pipx pipx ensurepath Ubuntu ...

  8. sipp模拟uas发送update

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. 但是fs在处理update消息时候有BUG,为了复现问题,使用sipp模拟uas,发送update并发送DTMF码. 本文档记录si ...

  9. SpringMVC:文件上传和下载

    文件下载 ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文 使用ResponseEntity实现下载文件的功能 @RequestMapping(& ...

  10. anaconda-navigator 卡在 loading applications

    其实上学期开学就已经这样了,我又不用,再者我上课对这些依赖不大,就没管. 这几天想彻底搞定吧.现状了opencv,最后搞了半天,还是通过安装相应版本的.whl文件搞定了,无法conda install ...