计算机视觉讨论群162501053

收入囊中
  • Hough变换
  • 概率Hough变换
  • 自己实现Hough变换直线检測

葵花宝典
先看一下我实现的效果图


以下,我们进入Hough变换的原理解说。

看上图,我们知道,经过一点(x0,y0)的直线能够表示成y0 = mox + b0
反过来看方程,b = –x0m + y0
,于是我们从原来的坐标系转移到了Hough空间,m是横坐标,b是纵坐标


刚才提到,经过(x0,y0)的直线具有的特征是b = –x0m
+ y0,在Hough空间下也是一条直线,
那么经过(x1,y1)的直线具有的特征是b = -x1m + y1,在Hough空间下是还有一条直线。
两条直线的相交点的(m,b)就是经过(x0,y0)(x1,y1)的直线,这个应该能够理解吧。
于是就有了一个简单的想法,对于每个点,在Hough空间中都画出一条直线,对于每条直线经过的点,都填充在例如以下的 Hough空间中,看哪交点多,就能确定。我们用一个二维数组表示Hough空间,例如以下。最后就变成数哪些格子的值比較高。

可是,用m和b有局限性。由于m是能够取到无穷大的,所以这个特征仅仅在理论上可行...实际上我们不可能申请一个无限大的二维数组。

自然而然,我们想到了极坐标,在极坐标下,就没有这个限制了。
在极坐标下,我们的直线能够写成:
也就是:
经过点(x0,y0)的直线:
当x0 = 8, y0 = 6,我们有这种图
我们在以下仅仅考虑 而且 .

我们还有2个点, 
     ,就能够绘制出以下的图形
这3条直线相交于,
也就是说 ()
 是这3个点   共同经过的直线!

因此,我们有了算法雏形                                       

• 初始化H( Hough空间的二维数组)全为0
• 遍历图片的 (x,y) 
For θ = 0 to 360

      ρ = xcos θ + y sin θ

      H(θ, ρ) = H(θ,ρ) + 1

    end

end
• Find the value(s) of (θ, ρ)where H(θ, ρ)is a local maximum
• Thedetected line in the image is given by  ρ = xcos θ + y sin θ

看以下的图片,当都一条直线时,Hough空间的某个区域就会非常亮,取局部极大值就能够



一张更复杂的图片



初识API
C++: void HoughLines(InputArray image,
OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
 
  • image – 8-bit,单通道二值图(有可能被函数改变)
  • lines – 输出vector,是  的vector.  是距离原点距离 (图片左上角[0,0]处).  
     ).
  • rho – 累加器的半径resolution
  • theta – 累加器的theta resulution
  • threshold – 返回Hough空间中 (  ).的点
  • srn – For the multi-scale Hough transform, it is a divisor for the distance resolution rho .
    The coarse accumulator distance resolution is rho and the accurate accumulator resolution is rho/srn .
    If both srn=0 and stn=0 ,
    the classical Hough transform is used. Otherwise, both these parameters should be positive.
  • stn – For the multi-scale Hough transform, it is a divisor for the distance resolution theta.
C++: void HoughLinesP(InputArray image,
OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, doublemaxLineGap=0 )
 
  • image –8-bit,单通道二值图(有可能被函数改变)
  • lines – 输出向量是4-element vector  ,
      是起点  是终点
  • rho – Distance resolution of the accumulator in pixels.
  • theta – Angle resolution of the accumulator in radians.
  • threshold – Accumulator threshold parameter. Only those lines are returned that get enough votes (  ).
  • minLineLength – 最小长度,小于这个值不被觉得是线段
  • maxLineGap – 两个点之间最大的gap,当小于这个值两个点就被觉得是同一线段的点

荷枪实弹
还是先贴出官方sample
#include <cv.h>
#include <highgui.h>
#include <math.h> using namespace cv; int main(int argc, char** argv)
{
Mat src, dst, color_dst;
if( argc != 2 || !(src=imread(argv[1], 0)).data)
return -1; Canny( src, dst, 50, 200, 3 );
cvtColor( dst, color_dst, CV_GRAY2BGR ); #if 0
vector<Vec2f> lines;
HoughLines( dst, lines, 1, CV_PI/180, 100 ); for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0];
float theta = lines[i][1];
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
Point pt1(cvRound(x0 + 1000*(-b)),
cvRound(y0 + 1000*(a)));
Point pt2(cvRound(x0 - 1000*(-b)),
cvRound(y0 - 1000*(a)));
line( color_dst, pt1, pt2, Scalar(0,0,255), 3, 8 );
}
#else
vector<Vec4i> lines;
HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );
for( size_t i = 0; i < lines.size(); i++ )
{
line( color_dst, Point(lines[i][0], lines[i][1]),
Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 );
}
#endif
namedWindow( "Source", 1 );
imshow( "Source", src ); namedWindow( "Detected Lines", 1 );
imshow( "Detected Lines", color_dst ); waitKey(0);
return 0;
}

假如我们想检測直线,就能够用第一个API,由于这个API返回的是直线的两个參数

假设想检測图片中的线段,就用第二个API,由于这个 API返回的是起点和终点

以下看下我自己的实现,首先是弧度及结构体的定义
const double pi = 3.1415926f;
const double RADIAN = 180.0/pi; struct line
{
int theta;
int r;
};

接下来是变换到Hough空间,填充二维数组

vector<struct line> houghLine(Mat &img, int threshold)
{
vector<struct line> lines;
int diagonal = floor(sqrt(img.rows*img.rows + img.cols*img.cols));
vector< vector<int> >p(360 ,vector<int>(diagonal)); for( int j = 0; j < img.rows ; j++ ) {
for( int i = 0; i < img.cols; i++ ) {
if( img.at<unsigned char>(j,i) > 0)
{
for(int theta = 0;theta < 360;theta++)
{
int r = floor(i*cos(theta/RADIAN) + j*sin(theta/RADIAN));
if(r < 0)
continue;
p[theta][r]++;
}
}
}
} //get local maximum
for( int theta = 0;theta < 360;theta++)
{
for( int r = 0;r < diagonal;r++)
{
int thetaLeft = max(0,theta-1);
int thetaRight = min(359,theta+1);
int rLeft = max(0,r-1);
int rRight = min(diagonal-1,r+1);
int tmp = p[theta][r];
if( tmp > threshold
&& tmp > p[thetaLeft][rLeft] && tmp > p[thetaLeft][r] && tmp > p[thetaLeft][rRight]
&& tmp > p[theta][rLeft] && tmp > p[theta][rRight]
&& tmp > p[thetaRight][rLeft] && tmp > p[thetaRight][r] && tmp > p[thetaRight][rRight])
{
struct line newline;
newline.theta = theta;
newline.r = r;
lines.push_back(newline);
}
}
} return lines;
}

最后是画直线的函数

void drawLines(Mat &img, const vector<struct line> &lines)
{
for(int i = 0;i < lines.size();i++)
{
vector<Point> points;
int theta = lines[i].theta;
int r = lines[i].r; double ct = cos(theta/RADIAN);
double st = sin(theta/RADIAN); //r = x*ct + y*st
//left
int y = int(r/st);
if(y >= 0 && y < img.rows){
Point p(0, y);
points.push_back(p);
}
//right
y = int((r-ct*(img.cols-1))/st);
if(y >= 0 && y < img.rows){
Point p(img.cols-1, y);
points.push_back(p);
}
//top
int x = int(r/ct);
if(x >= 0 && x < img.cols){
Point p(x, 0);
points.push_back(p);
}
//down
x = int((r-st*(img.rows-1))/ct);
if(x >= 0 && x < img.cols){
Point p(x, img.rows-1);
points.push_back(p);
} cv::line( img, points[0], points[1], Scalar(0,0,255), 1, CV_AA);
}
}

完整代码

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <vector>
#include <cmath>
using namespace cv;
using namespace std; const double pi = 3.1415926f;
const double RADIAN = 180.0/pi; struct line
{
int theta;
int r;
}; /*
* r = xcos(theta) + ysin(theta)
*/
vector<struct line> houghLine(Mat &img, int threshold)
{
vector<struct line> lines;
int diagonal = floor(sqrt(img.rows*img.rows + img.cols*img.cols));
vector< vector<int> >p(360 ,vector<int>(diagonal)); for( int j = 0; j < img.rows ; j++ ) {
for( int i = 0; i < img.cols; i++ ) {
if( img.at<unsigned char>(j,i) > 0)
{
for(int theta = 0;theta < 360;theta++)
{
int r = floor(i*cos(theta/RADIAN) + j*sin(theta/RADIAN));
if(r < 0)
continue;
p[theta][r]++;
}
}
}
} //get local maximum
for( int theta = 0;theta < 360;theta++)
{
for( int r = 0;r < diagonal;r++)
{
int thetaLeft = max(0,theta-1);
int thetaRight = min(359,theta+1);
int rLeft = max(0,r-1);
int rRight = min(diagonal-1,r+1);
int tmp = p[theta][r];
if( tmp > threshold
&& tmp > p[thetaLeft][rLeft] && tmp > p[thetaLeft][r] && tmp > p[thetaLeft][rRight]
&& tmp > p[theta][rLeft] && tmp > p[theta][rRight]
&& tmp > p[thetaRight][rLeft] && tmp > p[thetaRight][r] && tmp > p[thetaRight][rRight])
{
struct line newline;
newline.theta = theta;
newline.r = r;
lines.push_back(newline);
}
}
} return lines;
} void drawLines(Mat &img, const vector<struct line> &lines)
{
for(int i = 0;i < lines.size();i++)
{
vector<Point> points;
int theta = lines[i].theta;
int r = lines[i].r; double ct = cos(theta/RADIAN);
double st = sin(theta/RADIAN); //r = x*ct + y*st
//left
int y = int(r/st);
if(y >= 0 && y < img.rows){
Point p(0, y);
points.push_back(p);
}
//right
y = int((r-ct*(img.cols-1))/st);
if(y >= 0 && y < img.rows){
Point p(img.cols-1, y);
points.push_back(p);
}
//top
int x = int(r/ct);
if(x >= 0 && x < img.cols){
Point p(x, 0);
points.push_back(p);
}
//down
x = int((r-st*(img.rows-1))/ct);
if(x >= 0 && x < img.cols){
Point p(x, img.rows-1);
points.push_back(p);
} cv::line( img, points[0], points[1], Scalar(0,0,255), 1, CV_AA);
}
} int main( int, char** argv )
{
Mat src,src_gray,edge;
src = imread( argv[1] );
cvtColor( src, src_gray, CV_BGR2GRAY ); blur( src_gray, src_gray, Size(3,3) );
Canny( src_gray, edge, 50, 200);
vector<struct line> lines = houghLine(edge, 90);
drawLines(src, lines); namedWindow("result", 1);
imshow("result", src);
waitKey(); return 0;
}


举一反三

概率Hough变换在基本算法上添加了比較少的改动。之前是逐行扫描,如今则是随机选点。每当累加器的一个条目达到指定的最小值,就沿这条直线的方向扫描,并且将通过它的全部点删除(即使它们还没有參与投票)。并且该扫描还确定被接受的线段的长度。为此,该算法定义了两个附加參数。一个是被接受线段的最小长度,而还有一个是被同意以形成连续的段的最大距离。这个附加步骤添加了算法的复杂性,可是复杂性带来的效率损失被较少的点会參与投票过程补偿。

我们再来看一看其它形状在二维 Hough空间的样子



我们再考虑一下噪声的影响


噪声使得峰值定位非常难

噪声程度越厉害,结果越不准确

解决噪声问题的算法:

这个算法也不复杂,对于一个点,我们曾经要遍历[0,360]的角度,可是如今,这个角度就直接被我们取出来了,速度也有非常大的提升,非常不错的算法。

OpenCV2马拉松第22圈——Hough变换直线检測原理与实现的更多相关文章

  1. openCV2马拉松第19圈——Harris角点检測(自己实现)

    计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/26824529 收入囊中 使用OpenCV的con ...

  2. OpenCV2马拉松第15圈——边缘检測(Laplace算子,LOG算子)

    收入囊中 拉普拉斯算子 LOG算子(高斯拉普拉斯算子) OpenCV Laplacian函数 构建自己的拉普拉斯算子 利用拉普拉斯算子进行图像的锐化 葵花宝典 在OpenCV2马拉松第14圈--边缘检 ...

  3. OpenCV2马拉松第17圈——边缘检測(Canny边缘检測)

    计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g 收入囊中 利用OpenCV Canny函数进行边缘检測 掌握Canny算法基本理论 ...

  4. OpenCV2马拉松第14圈——边缘检測(Sobel,prewitt,roberts)

    收入囊中 差分在边缘检測的角色 Sobel算子 OpenCV sobel函数 OpenCV Scharr函数 prewitt算子 Roberts算子 葵花宝典 差分在边缘检測究竟有什么用呢?先看以下的 ...

  5. OpenCV2马拉松第25圈——直线拟合与RANSAC算法

    计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/28118095 收入囊中 最小二乘法(least ...

  6. OpenCV2马拉松第5圈——线性滤波

    收入囊中 这里的非常多内容事实上在我的Computer Vision: Algorithms and ApplicationsのImage processing中都有讲过 相关和卷积工作原理 边界处理 ...

  7. openCV2马拉松第18圈——坐标变换

    计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g 收入囊中 仿射变换 坐标映射 利用坐标映射做一些效果,例如以下 watermark/ ...

  8. OpenCV2马拉松第9圈——再谈对照度(对照度拉伸,直方图均衡化)

    收入囊中 lookup table 对照度拉伸 直方图均衡化 葵花宝典 lookup table是什么东西呢? 举个样例,假设你想把图像颠倒一下,f[i] = 255-f[i],你会怎么做? for( ...

  9. OpenCV2马拉松第2圈——读写图片

    收入囊中 用imread读取图片 用nameWindow和imshow展示图片 cvtColor彩色图像灰度化 imwrite写图像 Luv色彩空间转换 初识API 图像读取接口 image = im ...

随机推荐

  1. ZOJ 3817 Chinese Knot

    题意:给定4个长度为N的字符串( N <= 100000),然后构成一个“中国结”,给定目标串,问能否从某个节点出发走一遍得到目标串,其中不能连续通过3个中心节点,也就是从字符串一个端点转移到其 ...

  2. ANDROID_MARS学习笔记_S01原始版_015_Socket

    一.代码1.xml(1)main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayo ...

  3. MFC浅析(7) CWnd类虚函数的调用时机、缺省实现

    CWnd类虚函数的调用时机.缺省实现 FMD(http://www.fmdstudio.net) 1. Create 2. PreCreateWindow 3. PreSubclassWindow 4 ...

  4. idea快捷方式

    1. [常规] Ctrl + Shift + Enter,语句完成 "!",否定完成,输入表达式时按 "!"键 Ctrl + E,最近的文件 Ctrl+Shif ...

  5. 【POJ】1505 Copying Books

    此题主要采用DP思路,难点是求解"/",需要考虑划分数量不够的情况,先采用DP求解最优解,然后采用贪心求解slash.防止中间结果出错,使用了unsigned int. #incl ...

  6. Oracle Form Developer: Folder FRM-99999 Error 14212

    Question: 做FOLDER文件夹功能,打开FORM错误提示: FRM-99999:出现1412错误.有关该错误的详细信息,请参阅发行说明文件(relnotes) Answer: 原因是FOLD ...

  7. Centos4.3安装MySQL-python-1.2.3,出现error: command 'gcc' failed with exit status 1

    在Linux Centos 4.3上安装MySQL-python-1.2.3的时候出现error: command 'gcc' failed with exit status 1, 具体原因是因为没有 ...

  8. bzoj1143 2718

    最小可相交路径覆盖 先预处理可到达的点然后转化为最小不相交路径覆盖 type node=record        point,next:longint;      end; ..] of node; ...

  9. Linux Kernel‘ieee80211_radiotap_iterator_init()’函数拒绝服务漏洞

    漏洞名称: Linux Kernel‘ieee80211_radiotap_iterator_init()’函数拒绝服务漏洞 CNNVD编号: CNNVD-201312-041 发布时间: 2013- ...

  10. 求助:IIS中部署WCF,生成的WSDL中怎么把“计算机名”改成IP==找到一个解决办法

    环境:win2003 IIS6 VS2008 求助: 如图: 有朋友遇到过这个问题吗?还是说这个不是问题? 先 谢谢了! 补充配置文件: 代码 目前解决办法: 修改IIS的配置: 如图: 解决后的ws ...