一、霍夫变换Hough

Hough变换是图像处理中从图像中识别几何形状的基本方法之一。Hough变换的基本原理在于利用点与线的对偶性,将原始图像空间的给定的曲线通过曲线表达形式变为参数空间的一个点。这样就把原始图像中给定曲线的检测问题转化为寻找参数空间中的峰值问题。

二、霍夫空间

在一个xOy 的坐标系空间里,经过(x1,y1)的直线有无数条,我们可以用a(斜率)和b(截距)来表示        yi=axi+b,如果我们将这个(x1,y1)当成一个常量,那么我们可以用(x1,y1)来表示a和b,因此我们吧aOb构成的空间称之为霍夫空间。

b=-x1a+y1,图像表示如下,右边的就是霍夫空间,我们也称之为参数空间

假如有直线是y=x, 取上面的三个点:A(0,0), B(1,1), C(22)。

那么过A点的直线的参数是:0=0+b--L1,

过B点的直线的参数要满足方程:1=k+b --L2,

过C点的直线的参数要满足方程:2=2k+b--L3,

L1、L2、L3 对应着参数平面上的三条直线,而这三条直线会相交于一点(k=1,b=0)。把图像平面上的点对应到参数平面上的线,若xOy上有两条直线,那么在霍夫空间就会看到两个峰值点。

霍夫变换的思想

在xOy坐标系下的一个点在参数坐标系中的一条直线,反之一样,在xOy坐标系下一条直线的所有点,它们的斜率和截距是相同的,所以它们在参数坐标系下对应于同一个点

三、霍夫直线

如上图所示,可以转换到极坐标空间(数学推导就不写了)。

如何验证xoy 平面内的(x1,y 1),(x2, y 2),…是否共线?

只要曲线ρ=xi  cosθ+y i  sinθ,i=1,2,…在θoρ平面内相交于一个点 就可以了,如下图

如何在xoy平面找出哪些点在同一条直线上?

若xoy 平面内有5个点,对应到θoρ平面(霍夫空间)的5条 曲线,可以看出1、2、3、4点对应的曲线是相交于一个点的,所以xoy 平面内的1、2、 3、4点是共线的;同样,5点和4点对应的曲线也相交于一个点,所以xoy 平面内的5点和4 点是共线的,如下图:

结 论 :

       要判断xoy 平面内哪些点是共线的,首先求出每一个点对应到霍夫空间的曲线,然后判断哪几条曲线相交于一点,最后将相交于一点的曲线反过来对应到xoy 平面内 的点,这些点就是共线的,这就是在图像中进行标准霍夫直线检测的核心思想

c++实现解析

首先,霍夫直线处理的是一个二值图像,并且一般都是黑色背景

投票器

那么如何利用计算出的θoρ空间中的这些点去验证哪些像素点是共线的呢?是用曲线相交方式来验证的,但是这里只是从曲线上取了一些离散的点,所以需要 引入一个工具,称为“计数器”,或者“投票器”,或者二维直方图。

如上面的例子的投票结果如下:

构造霍夫空间中的计数器

若在xoy 平面内有任意一点(x1,y 1),过该点有无数条直线,但是原点到这些直线的距离不会超过sqrt(pow(x1,2)+pow(y1,2))=r。图像矩阵宽度为W、高度为H,那么可以构造以下计数器,用L代表整数r+1:

若图像宽度为10、高度为10,所以L=r+1=15,四个前景像素点的坐标分别为(6,4)、(5,5)、(3,7)、(2,8),那么所有霍夫 空间中的点坐标一共有4×180个(步长=1),(45°,round(6 cos 45°+4 sin 45°))=(45°, 7),(45°,round(5 cos 45°+5 sin 45°))=(45°,7),(45°,round(3 cos 45°+7 sin 45°))=(45°,7),(45°,round(2 cos 45°+8 sin 45°))=(45°,7)等,投票计数器中,在计数器(45°,7)这个位置的计数是4,这里的(45°,7)出现了4次,表明有四个像素点是共线的。

实现步骤

1)读取一幅带处理二值图像,最好背景为黑色;

2)获取图像空间的源像素数据;

3)通过量化霍夫参数空间为有限个值间隔等分或者累加格子,即r,θ;

4)霍夫变换算法开始,每个像素坐标点P(x, y)被转换到(r, θ)的曲线点上面,并累加到对应的格子数据点;

5)寻找最大霍夫值,设置阈值,反变换到图像空间

霍夫直线算子

 //void HoughLinesP(//   InputArray image, //输入图像,即源图像,需为8位的单通道二进制图像// OutputArray lines,//OutputArray类型的lines ,经过调用HoughLinesP函数后后存储了检测到的线条的输出矢量,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2)  表示,其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的结束点//  double rho, // 角度步长//   double theta, // 以弧度为单位的角度精度的// int threshold, //累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中//    double minLineLength = 0,//有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。//    double maxLineGap = 0)//有默认值0,允许将同一行点与点之间连接起

效果如下

HoughLinesP算子实现

代码显示:(虽然出来了,但是没有阈值控制并且运行速度很慢,等了15秒)

map<vector<int>, vector<Point>> HTline(Mat I, Mat& accumulator, float steptheta = 1, float stepRho = 1)
{ // 1.投票器的创建与初始化int  rows = I.rows;int  cols = I.cols;// 可能出现得到最大的垂线的长度 就是在博客中写得L=r+1;int  L = round(sqrt(pow(rows - 1, 2.0) + pow(cols - 1, 2.0)));// 创建一个投票器int numtheta = int(180.0 / steptheta);// 也就是说将这个分成了180分角度来计算投票的结果int numrho = int(2 * L / stepRho + 1);// 投票初始化accumulator = Mat::zeros(Size(numtheta, numrho),CV_32SC1);//2、初始化一个map ,用来存放共线的点map<vector<int>, vector<Point>>  lines;for ( int   i = 0; i < numrho ; i++){for (int j = 0; j < numtheta ; j++){// template pair make_pair(T1 a, T2 b) { return pair(a, b); }lines.insert(make_pair(vector<int>(j,i),vector<Point>()));}}// 3、开始投票计数for (int row = 0; row < rows; row++){for (int col = 0; col < cols; col++){// 循环遍历每一个像素if (I.at<uchar>(Point(col,row))==255)// 我么默认是黑色背景下的找白色{   // 相当于是遍历180度,对每一度计算rho的值for (int  i = 0; i < numtheta; i++){float   rhocol = col * cos(steptheta * i / 180.0 * CV_PI);float   rhorow = row * sin(steptheta * i / 180.0 * CV_PI);float  rho = rhocol + rhorow;// 计算投票到那个区域了int  n = int(round(rho + L) / stepRho);// 累加1 accumulator.at<int>(n,i)+=1;// 将这个点放到lines 中记录起来lines.at(vector<int>(i, n)).push_back(Point(col, row));}}}}return  lines;
}
Mat  grayACCU;accum.convertTo(grayACCU,CV_32FC1,1.0/ maxvalue);imshow("投票器的灰度级别显示",grayACCU);// 显示投票器大于某一个阈值的直线int vote = 150;for (int r = 0; r < accum.rows; r++){for (int c = 0; c < accum.cols; c++){int  current = accum.at<int>(r,c);//画直线  line 的首尾元素为起始点if (current>vote){int lt = accum.at<int>(r - 1, c - 1);// 左上int t= accum.at<int>(r - 1, c );//正上方的int rt = accum.at<int>(r - 1, c + 1);// 右上角int l = accum.at<int>(r , c - 1);//左int right = accum.at<int>(r , c + 1);// 右int lb = accum.at<int>(r + 1, c - 1);//左下角的int b = accum.at<int>(r - 1, c );// 下int rb = accum.at<int>(r + 1, c + 1);//右下// 判断这个位置是不是局部的最大值if (current > lt && current > t&&current > rt && current > l &&current > right && current > lb &&current > b && current > rb){vector<Point> line_s=lines.at(vector<int>(c,r));int s = line_s.size();// 划线line(src, line_s.at(0), line_s.at(s-1),Scalar(255),2);}}}}imshow("test_hough_line",src);

四、霍夫圆检测

若圆的圆心坐标是(a,b),半径为r,则圆在xoy平面内的方程可表示为:(x- a)2+(y -b)2=r2,那反过来呢?

如何求圆的圆心?

若有三个点(1,3)、(2,2)、(3,3)分别带入到上面的(x- a)2+(y -b)2=r2中,得到三个关于aOb的方程式,在霍夫空间中画出这三个圆得到下图:

当发生上图中右边的这种情况的时候我们是无法判断那个才是我们想要的圆心,这个时候我们需要引进第三维度的r来讨论一下,所以对于任意一个点(xi,y i)对应到abr空间中的锥面r2=(a- xi)2+(b-y i)2,那么如果多个锥面相交于一点(a′ ,b′ ,r′ ),则说明这个锥面对应着xoy平面内的点是共圆的并且圆心是(a′ ,b′ ),半径是r'

 该过程相当于先固定r,然后转换为以上讨论的已知r的情况,即第二个问题是第三个 问题的一种特殊情况。与霍夫直线检测类似,图像的霍夫圆检测就是检测哪些前景或边缘像素点在同一个 圆上,并给出对应圆的圆心坐标及圆的半径;而且仍然需要计数器来完成该过程,只是 这里的计数器从二维变成了三维。

三维计数器如何解析

若图像I,宽度为W、高度为H, 一 般首先指定需要检测到的圆的半径范围 ,寄假设人[rmin,rmax],这样的话,我们大概可以计算出圆心(a,b)的大致取值范围,这样的话(a,b,r)的大致范围都会有了,除此之外我们还需要一个参数-》变换的步长   若有个亮点像素坐标(x1,y1),每次固定一个r,可以用极坐标公式转换计算a=x1-r cosθ,b=y 1-r sinθ。                                     但是三维的投票器计算就很拖时间,我记得上次做直线检测的时候时间大概是15s,那个是二维的,检测圆是三维时间不是更慢,所以看到书上第二种方法。

基于梯度方向的霍夫圆检测

基于梯度的霍夫圆检测的大体步骤是,一、定位圆心(两个参数),二、计算半径 (一个参数)。在代码实现中,首先构造一个二维计数器,然后再构造一个一维计数器

 输入的图像不用像函数 HoughLinesP和HoughLines一样必须是二值图

void HoughCircles(
InputArray image,// 输入图像矩阵,要求是灰度图像但是不一定是二值图像
OutputArray circles,// circles是一个包含检测到的圆的信息的向量(a,b,r)
int method,//圆检测算法CV_HOUGH_GRADIENT 就是上面介绍的梯度算法
double dp,//投票器的分辨率,dp=2时投票器是元素图像的一半,宽高都缩减为原来的一半,dp=1时,1:1double minDist, //两个圆心之间的最小距离
double param1=100, //canny 算子的高阈值 低阈值为高阈值的一半;double param2=100, //最小投票数,(就是那个投票数量,大于设的设个值就当成是是圆上的一个像素)
int minRadius=0, 检测到的圆的半径最小值
int maxRadius=0 );检测到的圆的半径最大值

代码显示:


int main(int args, char* arg)
{const  char* OUTPUT_TITLE = "Hough Reult";Mat src, src_gray, dst,srcout;// point  src = imread("C:\\Users\\19473\\Desktop\\opencv_images\\116.jpg");if (!src.data){printf("could not  load  image....\n");}namedWindow("input_demo", CV_WINDOW_AUTOSIZE);namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);imshow("input_demo", src);// 1 做一个中值滤波  去噪medianBlur(src, srcout,3);// 2 转为8位的灰度图cvtColor(srcout, srcout,CV_BGR2GRAY);// 3  做霍夫圆变换vector<Vec3f>  pCircles;// (a,b,r)HoughCircles(srcout, pCircles,CV_HOUGH_GRADIENT,1,// 分辨率 20,//两个圆心之间的最小距离100,//canny 算子的高阈值 低阈值为高阈值的一半;30, //最小投票数,(就是那个投票数量,大于设的设个值就当成是是圆上的一个像素)5,//检测到的圆的半径最小值60);//检测到的圆的半径最da 值//void HoughCircles(//  InputArray image,// 输入图像矩阵,要求是灰度图像但是不一定是二值图像//   OutputArray circles,// circles是一个包含检测到的圆的信息的向量(a,b,r)// int method,//圆检测算法CV_HOUGH_GRADIENT 就是上面介绍的梯度算法//   double dp,//投票器的分辨率,dp=2时投票器是元素图像的一半,宽高都缩减为原来的一半,dp=1时,1:1//   double minDist, //两个圆心之间的最小距离// double param1 = 100, //canny 算子的高阈值 低阈值为高阈值的一半;//   double param2 = 100, //最小投票数,(就是那个投票数量,大于设的设个值就当成是是圆上的一个像素)//  int minRadius = 0, 检测到的圆的半径最小值//   int maxRadius = 0, 检测到的圆的半径最da 值src.copyTo(dst);Scalar  color = Scalar(0, 0, 255);for (size_t i = 0; i < pCircles.size(); i++){Vec3f cc = pCircles[i];circle(dst, Point(cc[0],cc[1]),cc[2],color,2,LINE_AA);   // 显示圆circle(dst, Point(cc[0], cc[1]), 2, Scalar(0, 255,0), 2, LINE_AA);  // 检测圆心}imshow(OUTPUT_TITLE, dst);waitKey(0);return 0;

待续。。。。。

Opencv 笔记8 霍夫变换相关推荐

  1. Python-OpenCV 笔记3 -- 霍夫变换(Hough)

    Python-OpenCV 笔记2 – 霍夫变换(Hough) 1.标准霍夫变换 HoughLinesP 函数原型: HoughLines(image, rho, theta, threshold, ...

  2. OpenCV 笔记 -- 边缘检测(Sobel、Laplace、Canny)

    OpenCV 笔记 – 边缘检测(Sobel.Laplace.Canny) 参考文档 一.Sobel 算子 1.简介 Sobel 算子是一个离散的一阶差分算子,用来计算图像亮度函数的一阶梯度近似值.在 ...

  3. opencv笔记(3):图像镜像

    生活就像大海,我就像一条咸鱼,在浩瀚的海洋中边浪边学,这是opencv笔记系列中的「图像镜像」.更多笔记可关注「浪学」公众哦 ~ 世间万图,皆有镜像.这一篇文章以很咸鱼的方式把它们记录下来. 首先,是 ...

  4. opencv笔记(6):彩色图像直方图

    生活就像大海,我就像一条咸鱼,在浩瀚的海洋中边浪边学,这是opencv笔记系列中的「彩色图像直方图」.更多可参观[浪学]公众号~ 颜色直方图是在许多图像检索系统中被广泛采用的颜色特征.它所描述的是不同 ...

  5. python+OpenCV笔记(二十四):Shi-Tomasi角点检测

    Shi-Tomasi角点检测 原理 python+OpenCV笔记(二十二):角点检测原理(Harris角点检测原理.Shi-Tomasi角点检测原理)https://blog.csdn.net/qq ...

  6. Opencv 笔记5 边缘处理-canny、sobel、Laplacian、Prewitt

    一.边缘检测概述 边缘检测是计算视觉中的基本问题,边缘检测的目的是标识图像中亮度变换明显的点.边缘检测大幅度的减少了图像的数据量(分为两种:灰度图像边缘检测和彩色图像边缘检测),并且剔除了不相关的信息 ...

  7. OpenCV笔记-图像预处理1

    OpenCV笔记 一. 图像预处理 1. 图像显示与存储 1.1 颜色空间 颜色空间(RGB) 加法混色 三通道:RGB 一个像素的颜色值:(b,g,r) 取值范围:[0,255] or [0.0,1 ...

  8. opencv笔记(二)之opencv打开笔记本摄像头

    一.前言 楼主最近在使用opencv采集摄像头信息,一开始都不知道怎么去打开笔记本的摄像头的,于是有空做一个记录分享 至于win下怎么配置opencv可以参考楼主文章Opencv笔记(一)之vs201 ...

  9. OpenCV笔记—进阶篇(图像效果处理)

    OpenCV笔记-进阶(图像效果处理) 此章节是基于本人OpenCV笔记-基础篇的延续,如果对此章节有疑惑的话,可以先看基础篇学习一下. 通过此篇可以学习到美图秀秀等P图软件实现图片效果原理及实现过程 ...

最新文章

  1. MySQL这样写UPDATE语句,劝退
  2. Uber做出艰难决定:关掉AI实验室,彭博社:Uber没有梦想
  3. 【python进阶】_多线程多进程
  4. DOS BAT批处理定义变量
  5. 黑马训练营自学笔记(03)
  6. 解决Lync2010错误:无法同步通讯簿信息
  7. 2019 AI Bootcamp·Guangzhou 参会日记
  8. php科学计数法转string,php如何将科学计数法转数字
  9. Microsoft.Jet.Oledb.4.0 找不到提供者或未安裝問題
  10. MyBatis基础入门《十七》动态SQL
  11. linux挂载硬盘_玩客云刷机系统之挂载U盘/硬盘增加储存空间
  12. 移动端调试工具-Debuggap
  13. python-数据结构-大学生-航空订票系统
  14. erc20钱包下载_【重要公告】VNT Chain主网钱包使用指南
  15. 架构师必看-架构之美第15章伸缩性架构设计
  16. yuzu模拟器安装设置大全
  17. 网络技术学习:虚拟专用网络
  18. Centos Piranha安装过程
  19. Tektronix泰克DPO4054示波器
  20. Gradle sync failed: Could not find xxxx.xx 之 强制刷新Gradle dependencies

热门文章

  1. 为什么要有uboot?
  2. 英语语音篇 - 看词能读
  3. 禅与摩托车维修艺术_摩托车与编程之禅
  4. 2018.10.31-dtoj-4015-永琳的竹林迷径(path)
  5. 【数分】7. AB实验篇
  6. 硬件开发总结笔记一:电阻
  7. 系统错误null是什么意思_为什么NULL是错误的?
  8. 三跨考研浙江大学计算机,“三跨”考研的焦虑 你能承受多少
  9. 仿大逃杀源码_破咒不是您的典型大逃杀
  10. Kinect v2.0原理介绍之十三:面部帧获取