目录
一、霍夫变换简介
1、霍夫变换的原理
2、霍夫变换的优点
3、霍夫变换的缺点
4、霍夫变换的应用场景
5、使用霍夫变换的步骤
二、霍夫变换—直线检测
1、霍夫直线变换介绍
2、霍夫直线变换的例子
3、相关API学习(代码例子)
三、霍夫变换—圆检测
1、霍夫圆检测原理
2、相关API学习(代码例子)
一、霍夫变换简介
霍夫变换(Hough Transform)是一种在图像处理中常用的技术,用于检测图像中的直线、圆或其他形状。
在霍夫变换中,直线通常由两个参数表示:斜率和截距。对于每个图像中的点,都可以在参数空间中绘制出与该点相关的曲线。当多个点共线时,它们在参数空间中的曲线会相交于同一个点,从而形成一个峰值。通过寻找参数空间中的峰值,我们可以确定图像中存在的直线。
1、霍夫变换的原理
它的原理是将图像中的每个点转换为参数空间中的曲线或曲面,并通过对这些曲线或曲面进行累加来找到图像中的特定形状。
(1)霍夫空间:对于待检测的形状,霍夫变换将其在参数空间中表示。对于直线检测,参数空间通常是极坐标空间,其中两个参数分别表示直线的长度和角度。对于圆和椭圆检测,参数空间则包括圆心和半径或椭圆参数。
(2)累加过程:对于图像中的每个边缘点,通过遍历参数空间,将其转换为参数空间中的曲线或曲面。当多个边缘点共线或共圆时,在参数空间中会出现峰值。这些峰值表示了可能存在的形状。
(3)阈值和筛选:根据设定的阈值,选择参数空间中的峰值作为检测结果。通常选择峰值强度高于阈值的部分,并进行进一步的筛选和优化。
(4)反变换:根据检测到的参数,在原始图像中绘制出相应的形状。对于直线检测,可以通过计算两个端点的坐标;对于圆和椭圆检测,可以通过计算圆心和半径或椭圆参数。
通过以上步骤,霍夫变换可以在图像中检测出特定形状的位置和参数。
2、霍夫变换的优点
(1)霍夫变换是一种基于几何特征的图像处理方法,对于形状检测具有较好的鲁棒性。它不依赖于形状的大小、方向和位置,能够在图像中准确地检测出各种形状。
(2)霍夫变换可以应用于不同类型的形状检测,包括直线、圆、椭圆等。通过适当选择参数表示方式和累加过程,可以扩展到其他形状的检测。
(3)霍夫变换可以应用于图像中存在噪声或部分遮挡的情况下。通过合适的预处理和参数设置,可以提高对形状的检测准确性。
3、霍夫变换的缺点
(1)霍夫变换的计算复杂度较高,特别是对于大尺寸图像和复杂形状的检测。这会导致算法的执行时间较长,不适用于实时应用或对计算资源要求较高的场景。
(2)霍夫变换对图像中的噪声比较敏感,因此需要进行预处理来降低噪声的影响。这可能会引入额外的步骤和计算开销。
(3)霍夫变换对参数的选择较为敏感,不同形状的检测可能需要不同的参数设置。这对于非专业用户来说可能会增加使用的难度。
(4)霍夫变换在处理曲线或曲面交叉的情况下可能会出现误检测或漏检的问题。特别是当形状之间存在相似性或重叠时,可能会导致结果的不准确性。
总体而言,霍夫变换是一种强大的图像处理技术,但在实际应用中需要综合考虑其优点和缺点,并结合具体场景进行参数调整和算法改进。
4、霍夫变换的应用场景
(1)直线检测:霍夫变换最常用的应用之一是检测图像中的直线。通过将图像中的每个点转换为参数空间中的曲线,可以找到共线点形成的峰值,从而确定直线的位置和方向。
(2)圆检测:除了直线,霍夫变换也可以用于检测图像中的圆。通过将图像中的每个点转换为参数空间中的曲面,可以找到共圆心的曲面交点,从而确定圆的位置和半径。
(3)椭圆检测:类似地,霍夫变换还可以用于检测图像中的椭圆。通过将图像中的每个点转换为参数空间中的曲面,可以找到共椭圆参数的曲面交点,从而确定椭圆的位置、长轴和短轴。
(4)形状匹配:除了检测特定形状,霍夫变换还可以用于形状匹配。通过将待匹配形状在参数空间中表示,并与图像中的曲线或曲面进行匹配,可以找到与待匹配形状相似的图像区域。
5、使用霍夫变换的步骤
(1)边缘检测:首先对图像进行边缘检测,例如使用Canny边缘检测算法。这可以帮助减少噪声和提取出形状的边缘。
(2)参数空间设置:根据待检测的形状类型,选择合适的参数空间。对于直线检测,通常使用极坐标空间,其中两个参数分别表示直线的长度和角度。对于圆和椭圆检测,参数空间则包括圆心和半径或椭圆参数。
(3)累加过程:遍历边缘点,并将其转换为参数空间中的曲线或曲面。通过累加过程,找到在参数空间中出现峰值的位置,这些峰值表示可能存在的形状。
(4)阈值和筛选:根据设定的阈值,选择参数空间中的峰值作为检测结果。通常选择峰值强度高于阈值的部分,并进行进一步的筛选和优化。
(5)反变换:根据检测到的参数,在原始图像中绘制出相应的形状。对于直线检测,可以通过计算两个端点的坐标;对于圆和椭圆检测,可以通过计算圆心和半径或椭圆参数。
(6)参数调优:根据实际需求,可能需要对参数进行调优,以获得更好的检测结果。这包括阈值的选择、参数空间的分辨率等。
(7)结果分析和后处理:对于检测到的形状,可以进行进一步的分析和处理。例如,可以根据形状的位置和特征进行分类或其他操作。
需要注意的是:霍夫变换对图像中的噪声比较敏感,因此在应用之前通常需要进行预处理,如边缘检测或滤波操作。
二、霍夫变换—直线检测
1、霍夫直线变换介绍
(1)Hough Line Transform用来做直线检测
(2)前提条件 – 边缘检测已经完成 (cv::Canny,输入的是8位的图像可以是单通道或多通道,输出的是8位单通道的灰度图像,再通过threshold转化为二值化图像)
(3)平面空间到极坐标空间转换
极坐标也就是霍夫空间的坐标。
2、霍夫直线变换的例子
(1)对于任意一条直线上的所有点来说
(2)变换到极坐标中,从[0~360]空间,可以得到r的大小
(3)属于同一条直线上点在极坐标空(r, theta)必然在一个点上有最强的信号出现,根据此反算到平面坐标中就可以得到直线上各点的像素坐标。从而得到直线
(4)从平面坐标变换到霍夫空间(极坐标)
3、相关API学习(代码例子)
(1)标准的霍夫变换 cv::HoughLines从平面坐标转换到霍夫空间,最终输出是极坐标空间;表示形式为:。
cv::HoughLines(
InputArray src, // 输入图像,必须8-bit的灰度图像
OutputArray lines, // 输出的极坐标来表示直线
double rho, // 生成极坐标时候的像素扫描步长
double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180
int threshold, // 阈值,只有获得足够交点的极坐标点才被看成是直线
double srn=0;// 是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
double stn=0;//是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
double min_theta=0; // 表示角度扫描范围 0 ~180之间, 默认即可
double max_theta=CV_PI
) // 一般情况是有经验的开发者使用,需要自己反变换到平面空间(少用)
(2)霍夫变换直线概率 cv::HoughLinesP最终输出是直线的两个点(常用)
cv::HoughLinesP(
InputArray src, // 输入图像,必须8-bit的灰度图像
OutputArray lines, // 输出的极坐标来表示直线
double rho, // 生成极坐标时候的像素扫描步长
double theta, //生成极坐标时候的角度步长,一般取值
CV_PI/180 int threshold, // 阈值,只有获得足够交点的极坐标点才被看成是直线
double minLineLength=0;// 最小直线长度
double maxLineGap=0;// 最大间隔
)
(3)检测结果
(4)代码演示:
#include<opencv2opencv.hpp> #include<iostream> #include <math.h> using namespace cv; using namespace std; // Hough Line霍夫直线检测 int main(int argc, char** argv) { Mat src, src_gray, dst; src = imread("line.jpg"); if (!src.data) { printf("could not load image... "); return -1; } char INPUT_TITLE[] = "input image"; char OUTPUT_TITLE[] = "hough-line-detection"; namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE); namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE); imshow(INPUT_TITLE, src); // extract edge Canny(src, src_gray, 150, 200); cvtColor(src_gray, dst, CV_GRAY2BGR); imshow("edge image", src_gray); vector<Vec2f> lines; HoughLines(src_gray, lines, 1, CV_PI / 180, 150, 0, 0); for (size_t i = 0; i < lines.size(); i++) { float rho = lines[i][0]; // 极坐标中的r长度 float theta = lines[i][1]; // 极坐标中的角度 Point pt1, pt2; double a = cos(theta), b = sin(theta); double x0 = a * rho, 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)); line(dst, pt1, pt2, Scalar(0, 0, 255), 1, CV_AA); } /* vector<Vec4f> plines; HoughLinesP(src_gray, plines, 1, CV_PI / 180.0, 10, 0, 10); Scalar color = Scalar(0, 0, 255); for (size_t i = 0; i < plines.size(); i++) { Vec4f hline = plines[i]; line(dst, Point(hline[0], hline[1]), Point(hline[2], hline[3]), color, 3, LINE_AA); }*/ imshow(OUTPUT_TITLE, dst); waitKey(0); return 0; }
效果展示:
三、霍夫变换—圆检测
1、霍夫圆检测原理
(1)从平面坐标到极坐标转换三个参数
(2)假设平面坐标的任意一个圆上的点,转换到极坐标中: 处有最大值,霍夫变换正是利用这个原理实现圆的检测。
2、相关API学习(代码例子)
(1)cv::HoughCircles
因为霍夫圆检测对噪声比较敏感,所以首先要对图像做中值滤波。
基于效率考虑,Opencv中实现的霍夫变换圆检测是基于图像梯度的实现,分为两步:
1)检测边缘,发现可能的圆心
2)基于第一步的基础上从候选圆心开始计算最佳半径大小
HoughCircles(
InputArray image, // 输入图像 ,必须是8位的单通道灰度图像
OutputArray circles, // 输出结果,发现的圆信息
Int method, // 方法 - HOUGH_GRADIENT
Double dp, // dp = 1;
Double mindist, // 10 最短距离-可以分辨是两个圆的,否则认为是同心圆- src_gray.rows/8 Double param1, // canny edge detection low threshold
Double param2, // 中心点累加器阈值 – 候选圆心
Int minradius, // 最小半径 Int maxradius//最大半径
)
(2)代码演示:
#include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespace cv; using namespace std; int main(int argc, char** argv) { Mat src, dst; src = imread("circle.jpg"); if (!src.data) { printf("could not load image... "); return -1; } char INPUT_TITLE[] = "input image"; char OUTPUT_TITLE[] = "hough circle demo"; namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE); namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE); imshow(INPUT_TITLE, src); // 中值滤波 Mat moutput; medianBlur(src, moutput, 3); cvtColor(moutput, moutput, CV_BGR2GRAY); // 霍夫圆检测 vector<Vec3f> pcircles; HoughCircles(moutput, pcircles, CV_HOUGH_GRADIENT, 1, 10, 100, 30, 5, 50); src.copyTo(dst); for (size_t i = 0; i < pcircles.size(); i++) { Vec3f cc = pcircles[i]; circle(dst, Point(cc[0], cc[1]), cc[2], Scalar(0, 0, 255), 2, LINE_AA); circle(dst, Point(cc[0], cc[1]), 2, Scalar(198, 23, 155), 2, LINE_AA); } imshow(OUTPUT_TITLE, dst); waitKey(0); return 0; }