Hough Line Transform用来做直线检测,前提条件是边缘检测已经完成(Canny边缘检测),它是由平面空间到极坐标空间转换。
他是通过把图像上的所有像素点都变成极坐标空间,然后形成一条一条的曲线,如果所有的曲线都相交在一个点,那么这个点对应的和r值就表示直线的角度。这就说明在屏幕坐标上的直线,在极坐标空间找到了,我们拿到了和r重新反算到平面空间。
对于任意一条直线上的所有点来说,变换到极坐标中,从[0,360]空间,可以得到r的大小,属于同一条直线上点在极坐标空(r,theta)必然在一个点上有最强的信号出现,跟几乎此反算到屏幕坐标中就可以得到直线上个点的像素坐标,从而得到直线。
下面的API不建议使用:
建议使用的API:
void MyApi::HoughTranslate(Mat& image)
{
//做边缘检测首先要进行高斯边缘模糊,如果利用Canny高斯边缘模糊Canny会帮我们做
Mat image_gray, dst;
Canny(image, image_gray, 150, 200);//100,200分别是低阈值和高阈值输出二值图
imshow("edge_image", image_gray);
cvtColor(image_gray, dst, COLOR_GRAY2BGR);
vector plines;//吧每个像素点的平面坐标转化为极坐标产生的曲线放入集合中
HoughLinesP(image_gray, plines, 1, CV_PI / 180.0, 10, 0, 10);//从平面坐标转换到霍夫空间,最终输出的是直线的两个点(x0,y0,x1,y1)
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("hough_line_detection", dst);
}
结果如下所示:
上图分别是输入图像、边缘检测结果、以及霍夫直线检测的结果
从平面坐标圆上的点到极坐标转换的三个参数C(x0,y0,r)其中x0,y0是圆心,r 取一固定值时theta扫描360度,x y 跟着变化, 若多个边缘点对应的三维空间曲线交于一点,则他们在共同圆上,在圆心处有累积最大值,也可以用同样的阈值的方法来判断一个圆是否被检测到。
注:
因为霍夫圆检测对噪声比较敏感,所以首先要对图像做滤波(模糊降噪)。(比如椒盐噪声用中值滤波,其他的也可以用高斯模糊)
基于效率考虑,Opencv中实现的霍夫变换圆检测是基于图像梯度(霍夫梯度法, 也叫2-1霍夫变换(21HT))的实现,分为两步(已封装到HoughCircles):
第一步: Canny检测边缘,发现可能的圆心。圆心一定是在圆上的每个点的模向量上, 这些圆上点模向量的交点就是圆心, 霍夫梯度法的第一步就是找到这些圆心, 这样三维的累加平面就又转化为二维累加平面。
第二步:基于第一步的基础上从候选圆心开始计算最佳半径大小。第二步根据所有候选中心的边缘非0像素对其的支持程度来确定半径。
HoughCircles(
InputArray image, // 输入图像 ,必须是8位的单通道灰度图像,8位可以有4个通道,每个通道是8位,可以使RGB。
OutputArray circles, // 输出结果,发现的圆信息(数组)
Int method, // 选梯度方法 - HOUGH_GRADIENT
Double dp, // dp = 1,原图尺度(2的话比原图小一半来寻找,速度快了)
Double mindist, // 10 半径最短距离-可以分辨是两个圆的,否则认为是半径不同的同心圆- src_gray.rows/8
Double param1, // canny边缘检测的低阈值canny edge detection low threshold
Double param2, // 中心点累加器阈值 – 候选圆心
Int minradius, // 圆最小半径
Int maxradius//圆最大半径(范围越大,速度越慢)
)
void MyApi::HoughCircleTranslate(Mat& image)
{
Mat m_image,m_gray_image,dst;
//首先需要中值滤波
medianBlur(image, m_image,3);
cvtColor(m_image, m_gray_image, COLOR_BGR2GRAY);
//霍夫圆检测
vectorpcircles;//把可能的圆放进容器
HoughCircles(m_gray_image, pcircles, HOUGH_GRADIENT, 1, 10, 100,30,5,50);
image.copyTo(dst);
for (size_t i = 0; i < pcircles.size(); i++)
{
Vec3f cc = pcircles[i];//找到第i个圆,然后我们需要在dst图上画出来
//绘制圆
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(98, 23, 255), 2, LINE_AA);
}
imshow("输出结果", dst);
}
原始图像和输出结果如上图所示,圆形被检测出来