若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105575546
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)
上一篇:《OpenCV开发笔记(四十三):红胖子8分钟带你深入了解累计概率霍夫线变换(图文并茂+浅显易懂+程序源码)》
下一篇:《OpenCV开发笔记(四十五):红胖子8分钟带你深入了解重映射(图文并茂+浅显易懂+程序源码)》
红胖子来也!!!
去噪、边缘检测之后,就是特征提取了,识别图形的基本方法之一---霍夫变换,霍夫变换是图像处理中的一种特征提取技术,本篇章主要讲解霍夫圆变换。
前面2个Demo不怎么准确,是因为笔者对原图进行了缩放,导致原本的圆形有点变形了,最后一个则是专门按照缩放后的分辨率,自己绘制的。
霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,改过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果。
经典的霍夫变换用来检测图像中的直线,后来霍夫变换扩展到任意形状物体的识别,多为圆和椭圆。
霍夫变换运用两个坐标空间之间的变换将在一个空间中具有相同的形状的曲线或直线映射到另一个坐标控件的一个点上形成峰值,从而把检测任何形状的问题转化为统计峰值问题。
OpenCV中的霍夫变换分为两大类型,线变换下又分三种类型,如下图:
霍夫圆变换,从名字就可以知道其实针对圆,显而易见就是用来寻找圆的方法,此处特别注意,使用霍夫圆变换之前,肯定是需要对图片进行预处理:降噪、边缘检测处理,霍夫圆变换只寻找圆,只能识别边缘二值图像,所以输入也就只能是二值化(单通道8位)的图像了。
霍夫圆变换会找出大量的圆,但是有些圆其实是无用的,与霍夫线变换一样都可能会产生“噪声”数据。
在笛卡尔坐标系中圆的方式:
公式得出:
和
所以在abr组成的三维坐标系中,一个点可以唯一确定一个圆。
在笛卡尔的xy坐标系中经过某一点的所有圆映射到abr坐标系中就是一条三维的曲线。
经过xy坐标系中所有的非零像素点的所有圆就构成了abr坐标系中很多条三维的曲线。
在xy坐标系中同一个圆上的所有点的圆方程是一样的,它们映射到abr坐标系中的是同一个点,所以在abr坐标系中该点就应该有圆的总像素N0个曲线相交。通过判断abr中每一点的相交(累积)数量,大于一定阈值的点就认为是圆。
以上是标准霍夫圆变换实现算法,问题是它的累加面是一个三维的空间,意味着比霍夫线变换需要更多的计算消耗。
Opencv霍夫圆变换对标准霍夫圆变换做了运算上的优化。它采用的是“霍夫梯度法”。它的检测思路是去遍历累加所有非零点对应的圆心,对圆心进行考量。
圆心一定是在圆上的每个点的模向量上,即在垂直于该点并且经过该点的切线的垂直线上,这些圆上的模向量的交点就是圆心。
霍夫梯度法就是要去查找这些圆心,根据该“圆心”上模向量相交数量的多少,根据阈值进行最终的判断。
算法高效,并能够解决三维累加器中会产生许多噪声并且使得结果不稳定的稀疏不稳定问题;
void HoughCircles( InputArray image,
OutputArray circles,
int method,
double dp,
double minDist,
double param1 = 100,
double param2 = 100,
int minRadius = 0,
int maxRadius = 0);
序号 |
枚举 |
值 |
描述 |
1 |
HOUGH_STANDARD |
0 |
CV_HOUGH_STANDARD - 传统或标准 Hough 变换(SHT)。每一个线段由两个浮点数 (ρ, θ) 表示,其中 ρ 是直线与原点 (0,0) 之间的距离,θ 线段与 x-轴之间的夹角。因此,矩阵类型必须是 CV_32FC2 type; |
2 |
HOUGH_PROBABILISTIC |
1 |
CV_HOUGH_PROBABILISTIC- 概率 Hough 变换(PPHT)。如果图像包含一些长的线性分割,则效率更高。它返回线段分割而不是整个线段。每个分割用起点和终点来表示,所以矩阵(或创建的序列)类型是 CV_32SC4 type; |
3 |
HOUGH_MULTI_SCALE |
2 |
传统 Hough 变换的多尺度变种。线段的编码方式与 CV_HOUGH_STANDARD 的一致 |
4 |
HOUGH_GRADIENT |
3 |
基本上是 21HT |
void OpenCVManager::testHoughCircles()
{
QString fileName1 =
"E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/17.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 dp = 10; // 默认1像素
int minDist = 10; // 默认1°
int minRadius = 0;
int maxRadius = 0;
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,
srcMat.cols * 0 + 130,
200,
&threshold2,
0,
255);
qDebug() << __FILE__ << __LINE__;
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 - 80,
"dp = value / 10");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 1 + 50 - 80,
200,
&dp,
1,
1000);
cvui::printf(windowMat,
width * 1 + 100,
height * 1 + 100 - 80,
"minDist = value / 2");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 1 + 130 - 80,
200,
&minDist,
1,
720);
cvui::printf(windowMat,
width * 1 + 100,
height * 1 + 180 - 80,
"minRadius");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 1 + 210 - 80,
200,
&minRadius,
0,
100);
cvui::printf(windowMat,
width * 1 + 100,
height * 1 + 260 - 80,
"maxRadius");
cvui::trackbar(windowMat,
width * 1 + 100,
height * 1 + 290 - 80,
200,
&maxRadius,
0,
1000);
// 边缘检测后,进行霍夫圆检测
std::vector circles;
cv::HoughCircles(dstMat,
circles,
cv::HOUGH_GRADIENT,
dp / 10.0f,
minDist / 10.0f,
200,
100,
minRadius,
maxRadius);
// 在图中绘制出每条线段
dstMat = colorMat.clone();
for(int index = 0; index < circles.size(); index++)
{
cv::Point center(cvRound(circles[index][0]),
cvRound(circles[index][1]));
int radius = cvRound(circles[index][2]);
// 绘制圆心
cv::circle(dstMat, center, 3, cv::Scalar(255, 255, 255));
// 绘制圆
cv::circle(dstMat, center, radius, cv::Scalar(0, 0, 255));
}
// 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);
// 在图中绘制出每条线段
for(int index = 0; index < circles.size(); index++)
{
cv::Point center(cvRound(circles[index][0]),
cvRound(circles[index][1]));
int radius = cvRound(circles[index][2]);
// 绘制圆心
cv::circle(grayMat, center, 3, cv::Scalar(255, 255, 255));
// 绘制圆
cv::circle(grayMat, center, radius, cv::Scalar(0, 0, 255));
}
// copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, grayMat, 1.0f, 0.0f, mat);
}
// 更新
cvui::update();
// 显示
cv::imshow(windowName, windowMat);
// esc键退出
if(cv::waitKey(25) == 27)
{
break;
}
}
}
对应版本号v1.39.0
上一篇:《OpenCV开发笔记(四十三):红胖子8分钟带你深入了解累计概率霍夫线变换(图文并茂+浅显易懂+程序源码)》
下一篇:《OpenCV开发笔记(四十五):红胖子8分钟带你深入了解重映射(图文并茂+浅显易懂+程序源码)》
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105575546