OpenCV2马拉松第22圈——Hough变换直线检測原理与实现
- Hough变换
- 概率Hough变换
- 自己实现Hough变换直线检測
,于是我们从原来的坐标系转移到了Hough空间,m是横坐标,b是纵坐标
+ y0,在Hough空间下也是一条直线,
在极坐标下,我们的直线能够写成:


我们在以下仅仅考虑
而且
.
,
,
,就能够绘制出以下的图形
这3条直线相交于
,也就是说 (
)=
是这3个点
,
共同经过的直线!ρ = xcos θ + y sin θ
H(θ, ρ) = H(θ,ρ) + 1
end
end
-
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,当小于这个值两个点就被觉得是同一线段的点
#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返回的是直线的两个參数
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;
}
OpenCV2马拉松第22圈——Hough变换直线检測原理与实现的更多相关文章
- openCV2马拉松第19圈——Harris角点检測(自己实现)
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/26824529 收入囊中 使用OpenCV的con ...
- OpenCV2马拉松第15圈——边缘检測(Laplace算子,LOG算子)
收入囊中 拉普拉斯算子 LOG算子(高斯拉普拉斯算子) OpenCV Laplacian函数 构建自己的拉普拉斯算子 利用拉普拉斯算子进行图像的锐化 葵花宝典 在OpenCV2马拉松第14圈--边缘检 ...
- OpenCV2马拉松第17圈——边缘检測(Canny边缘检測)
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g 收入囊中 利用OpenCV Canny函数进行边缘检測 掌握Canny算法基本理论 ...
- OpenCV2马拉松第14圈——边缘检測(Sobel,prewitt,roberts)
收入囊中 差分在边缘检測的角色 Sobel算子 OpenCV sobel函数 OpenCV Scharr函数 prewitt算子 Roberts算子 葵花宝典 差分在边缘检測究竟有什么用呢?先看以下的 ...
- OpenCV2马拉松第25圈——直线拟合与RANSAC算法
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/28118095 收入囊中 最小二乘法(least ...
- OpenCV2马拉松第5圈——线性滤波
收入囊中 这里的非常多内容事实上在我的Computer Vision: Algorithms and ApplicationsのImage processing中都有讲过 相关和卷积工作原理 边界处理 ...
- openCV2马拉松第18圈——坐标变换
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g 收入囊中 仿射变换 坐标映射 利用坐标映射做一些效果,例如以下 watermark/ ...
- OpenCV2马拉松第9圈——再谈对照度(对照度拉伸,直方图均衡化)
收入囊中 lookup table 对照度拉伸 直方图均衡化 葵花宝典 lookup table是什么东西呢? 举个样例,假设你想把图像颠倒一下,f[i] = 255-f[i],你会怎么做? for( ...
- OpenCV2马拉松第2圈——读写图片
收入囊中 用imread读取图片 用nameWindow和imshow展示图片 cvtColor彩色图像灰度化 imwrite写图像 Luv色彩空间转换 初识API 图像读取接口 image = im ...
随机推荐
- POJ1177+线段树+扫描线
思路: 以y的值进行离散化 根据x的值 对每一条y轴边进行处理,如果是"左边"则插入,是"右边"则删除. /* 扫描线+线段树+离散化 求多个矩形的周长 */ ...
- JavaScript 按回车键提交搜索表单 easyui ajax方式
<!-- 搜索框 --> <form id="bUserSearchForm" style="padding:8px;" onkeydown= ...
- [模拟]Codeforces509C Sums of Digits
题目链接 题意:给n个数a[i], 要求b[i]每位数的和等于a[i], 并且b[i]要严格递增 求最小的b[i] b[0]最小一定是X9999...这样的形式 后面的b[i]位数一定大于等于前一个 ...
- <算法竞赛入门经典> 第8章 贪心+递归+分治总结
虽然都是算法基础,不过做了之后还是感觉有长进的,前期基础不打好后面学得很艰难的,现在才慢慢明白这个道理. 闲话少说,上VOJ上的专题训练吧:http://acm.hust.edu.cn/vjudge/ ...
- Azure HDInsight 现已在中国正式发布
今年月,我们宣布微软成为全球首家在中国公开发布云 Hadoop 产品公共预览版的云提供商.今天,微软非常高兴地宣布 AzureHDInsight现已在中国正式发布.中国本土组织以及在中国设立了办 ...
- Javascript 多线程?
最近在遇到一个问题:HTML页面中的页面无法刷新,只能在底层全部处理完成后才能进行页面刷新.在里面已经采用SetTimeout进行了处理,但是明显没有达到预期的效果. 主要的原因是对SetTimeou ...
- epub显示特殊字体
You need to open the ePub in an archive program (they are just ZIP files) and add an XML file to the ...
- JAVA与.NET的相互调用——通过Web服务实现相互调用
JAVA与.NET是现今世界竞争激烈的两大开发媒体,两者语言有很多相似的地方.而在很多大型的开发项目里面,往往需要使用两种语言进行集成开发.而很多的开发人员都会偏向于其中一种语言,在使用集成开发的时候 ...
- windows下rundll32介绍
最近看书介绍rundll32可以加载dll文件并执行其中导出函数,在MSDN中我们可以看到绍http://support.microsoft.com/kb/164787/zh-cn rundll32调 ...
- 关于 UGUI 字体花屏或乱码。
我们项目从某个时候开始ui突然开始出现字体花屏现象(unity 开发版本:5.3.6p6),而且很难必现却又时有发生,确实查找和解决起来不太容易. 关于这个问题,uwa官方给出了解释,http://b ...