若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105544972
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)
上一篇:《OpenCV开发笔记(四十二):红胖子8分钟带你深入了解标准霍夫线变换(图文并茂+浅显易懂+程序源码)》
下一篇:《OpenCV开发笔记(四十四):红胖子8分钟带你深入了解霍夫圆变换(图文并茂+浅显易懂+程序源码)》
红胖子来也!!!
去噪、边缘检测之后,就是特征提取了,识别图形的基本方法之一---霍夫变换,霍夫变换是图像处理中的一种特征提取技术,上一篇讲解了霍夫线变换中的标准霍夫变换,本篇章讲解霍夫线变换中的累计概率霍夫线变换。
此处会再次讲一遍原理,加深各位的理解,因为上一篇的8分钟理解霍夫变换不太够,霍夫变换是重要的检测手段之一,所以本篇再说一次原理,同时对两者的检测效果进行比较。
霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,改过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果。
经典的霍夫变换用来检测图像中的直线,后来霍夫变换扩展到任意形状物体的识别,多为圆和椭圆。
霍夫变换运用两个坐标空间之间的变换将在一个空间中具有相同的形状的曲线或直线映射到另一个坐标控件的一个点上形成峰值,从而把检测任何形状的问题转化为统计峰值问题。
OpenCV中的霍夫变换分为两大类型,线变换下又分三种类型,如下图:
霍夫线变换,从名字就可以知道其实针对直线,显而易见就是用来寻找直线的方法,此处特别注意,使用霍夫线变换之前,肯定是需要对图片进行预处理:降噪、边缘检测处理,霍夫线变换只寻找直线,只能识别边缘二值图像,所以输入也就只能是二值化(单通道8位)的图像了。
霍夫线变换分为三种,如下图:
霍夫线变换会找出大量的线,但是有些线其实是无用的。
霍夫变换采用极坐标的方式来表示直线。
因此直线的表达式是:
得出r公式为:
rθ= x0 * cosθ + y0 * sinθ
每一对(rθ,θ)代表一条通过点(x0,y0)的直线。
例如,对于给定点x0=8和y0=6,计算原理如下:
可以会出如下图曲线:
只绘出满足特定条件,如r>0和0<θ<2π,(注意:0表示垂直线,π/2度表示水平线);
如果两个不同点进行上述操作后得到的曲线在平面θ-r相交,这就意味着他们通过同一条直线。
例如,接上面的例子继续对点x1=9,y1=4和x2=12,y2=3绘图如下:
这三条曲线在平面相较于点(0.925, 9.6),坐标表示的是参数对θ-r或者是说点(x0, y0)、(x1, y1)和(x2, y2)组成的平面内的直线,所以其实是计算平面内的每个点的每个角度的距离,绘制成曲线后,如果3个点交叉了,那么3个点就是在一条直线上,示意如下图:
(交于一点的曲线的数量超过了阈值(在同一条直线上点的数量),比如上面的示意图,就认为3个点就可以组成一条直线,那么他们可以检测出一跳直线)
越多曲线交于一点也就意味着这个点焦点表示的直线有更多的点组成。一般来说我们可以通过设置直线上点的阈值来定义多少条曲线交于一点,这样才认为检测到了一条直线。
以上就是霍夫变换所做的,它追踪图像中每个点对应曲线的交点,如果交于一点的曲线的数量超过了阈值(在同一条直线上点的数量阈值),那么可以认为这个交点所代表的参数对(θ,rθ)在原图像中为一条直线。
void HoughLinesP( InputArray image,
OutputArray lines,
double rho,
double theta,
int threshold,
double minLineLength = 0,
double maxLineGap = 0 );
cvRound():返回跟参数最接近的整数值,即四舍五入;
cvFloor():返回不大于参数的最大整数值,即向下取整;
cvCeil():返回不小于参数的最小整数值,即向上取整;
void OpenCVManager::testHoughLinesP()
{
QString fileName1 =
"E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/16.jpg";
cv::Mat srcMat = cv::imread(fileName1.toStdString());
int width = 400;
int height = 300;
cv::resize(srcMat, srcMat, cv::Size(width, height));
cv::Mat colorMat = srcMat.clone();
cv::String windowName = _windowTitle.toStdString();
cvui::init(windowName);
cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2, srcMat.rows * 3),
srcMat.type());
cv::cvtColor(srcMat, srcMat, CV_BGR2GRAY);
int threshold1 = 200;
int threshold2 = 100;
int apertureSize = 1;
int rh0 = 1; // 默认1像素
int theta = 1; // 默认1°
int threshold = 100; // 默认检测到同一直线的100个点
int minLineLength = 50; // 检测线的最小长度
int maxLineGap = 10; // 同一条线点与点的最大距离
while(true)
{
qDebug() << __FILE__ << __LINE__;
windowMat = cv::Scalar(0, 0, 0);
cv::Mat mat;
cv::Mat dstMat;
cv::Mat grayMat;
// 转换为灰度图像
// 原图先copy到左边
cv::Mat leftMat = windowMat(cv::Range(0, srcMat.rows),
cv::Range(0, srcMat.cols));
cv::cvtColor(srcMat, grayMat, CV_GRAY2BGR);
cv::addWeighted(leftMat, 0.0f, grayMat, 1.0f, 0.0f, leftMat);
{
cvui::printf(windowMat,
width * 1 + 100,
height * 0 + 20,
"threshold1");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 0 + 50,
200,
&threshold1,
0,
255);
cvui::printf(windowMat,
width * 1 + 100,
height * 0 + 100, "threshold2");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 0 + 130,
200,
&threshold2,
0,
255);
cv::Canny(srcMat, dstMat, threshold1, threshold2, apertureSize * 2 + 1);
// copy
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::cvtColor(dstMat, grayMat, CV_GRAY2BGR);
cv::addWeighted(mat, 0.0f, grayMat, 1.0f, 0.0f, mat);
cvui::printf(windowMat,
width * 1 + 100,
height * 1 + 20 - 120,
"rho / 100");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 1 + 50 - 120,
200,
&rh0,
1,
1000);
cvui::printf(windowMat,
width * 1 + 100,
height * 1 + 100 - 120,
"theta = value / 2");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 1 + 130 - 120,
200,
&theta,
1,
720);
cvui::printf(windowMat,
width * 1 + 100,
height * 1 + 180 - 120,
"min points");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 1 + 210 - 120,
200,
&threshold,
2,
300);
cvui::printf(windowMat,
width * 1 + 100,
height * 1 + 260 - 120,
"minLineLength = value / 10");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 1 + 290 - 120,
200,
&minLineLength,
0,
1000);
cvui::printf(windowMat,
width * 1 + 100,
height * 1 + 340 - 120,
"maxLineGap = value / 10");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 1 + 370 - 120,
200,
&maxLineGap,
0,
1000);
// 边缘检测后,进行霍夫线检测
// 使用霍夫线变化检测所有角度范围内的直线
std::vector lines;
cv::HoughLines(dstMat, // 输入8位
lines, // 输出线 std::vector
rh0 / 100.0f, // 初步像素精度
theta / 720.0 * CV_PI, // 初步偏移角度精度
threshold, // 必须达到的点的数量
0, // 标准霍夫变换,为0
0, // 标准霍夫变换,为0
0, // 检测角度范围最小为0
CV_PI); // 检测角度范围最大为π,即360°
// 在图中绘制出每条线段
dstMat = colorMat.clone();
qDebug() << __FILE__ << __LINE__ << lines.size();
for(int index = 0; index < lines.size(); index++)
{
float rho = lines[index][0];
float theta = lines[index][1];
cv::Point pt1;
cv::Point pt2;
double a = cos(theta);
double b = sin(theta);
double x0 = a * rho;
double y0 = b * rho;
// 计算出点的坐标
pt1.x = cvRound(x0 + 1000 * (-b));
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));
// 画线
cv::line(dstMat, pt1, pt2, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
}
// copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
// 使用概率霍夫线变化检测所有长度范围内的直线
cv::Canny(srcMat, dstMat, threshold1, threshold2, apertureSize * 2 + 1);
std::vector lines2;
cv::HoughLinesP(dstMat, // 输入8位
lines2, // 输出线 std::vector
rh0/100.0f, // 初步像素精度
theta / 720.0 * CV_PI, // 初步偏移角度精度
threshold, // 必须达到的点的数量
minLineLength / 10.0f, // 检测线的最小长度
maxLineGap / 10.0f); // 检测线的最大距离
// 在图中绘制出每条线段
dstMat = colorMat.clone();
for(int index = 0; index < lines.size(); index++)
{
// 画线
cv::Vec4i line = lines2[index];
cv::line(dstMat,
cv::Point(line[0], line[1]),
cv::Point(line[2], line[3]),
cv::Scalar(0, 0, 255),
1,
cv::LINE_AA);
}
// copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
// 更新
cvui::update();
// 显示
cv::imshow(windowName, windowMat);
// esc键退出
if(cv::waitKey(25) == 27)
{
break;
}
}
}
对应版本号v1.38.0
https://blog.csdn.net/shenziheng1/article/details/75307410
上一篇:《OpenCV开发笔记(四十二):红胖子8分钟带你深入了解标准霍夫线变换(图文并茂+浅显易懂+程序源码)》
下一篇:《OpenCV开发笔记(四十四):红胖子8分钟带你深入了解霍夫圆变换(图文并茂+浅显易懂+程序源码)》
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105544972