目前想对于霍夫圆检测进行修改,想法是若能在固定圆心的横坐标的情景下去搜索圆,若要实现就需要对霍夫检测有一定的深入了解。
霍夫变换原理
霍夫变换原理实则就是参数空间的转变。
极坐标转换
首先因为直角坐标系中垂直于x轴的直线不存在,即转换用极坐标表示,即用ρ,θ ρ , θ 表示,对应于下图中的rho,degree。下图中x,y轴的画法是根据图像在窗口中显示的坐标方法。
用ρ,θ ρ , θ 表示的直线应该为:
推导方法:根据ρ ρ 是过坐标原点向已知直线做的垂线段,再根据角度,求取经过x,y轴的坐标点,然后根据两点之间的坐标公式求得。
参数空间的转换
将x,y的参数空间转化为 空间,这样做的目的是因为,将直角坐标的许多点,如若映射到ρ,θ ρ , θ 空间,会映射成许多正弦曲线,而其中许多曲线若相交于点,说明直角坐标系中许多个点共这一个,即找到一条直线。
找出ρ,θ 参数空间的最值
这里找出的最值应该事区域极值,但前期为了方便测试原理,只是简单在ρ,θ ρ , θ 矩阵中寻找最大值。将找到的最值转换为斜率截距并显示在图片中
注意点
1.需要注意的是霍夫变换输入的图片是二值化的图片,即若图片某点像素值为255,则代表该点存在,即可转换到ρ,θ
ρ
,
θ
空间
2.canny边缘检测的图片更容易检测出尖锐的边缘部分,更容易找到直线,而如果采用阈值函数二值化的话,直线上某些点可能会被设定的阈值过滤掉。
编程实现
#include<iostream>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
#define Blue Scalar(255,0,0)
//角度转弧度 π / 180×角度 弧度变角度 180 / π×弧度
#define Pi 3.1415926
void paint(Mat img, double k, double b)
{
for (int x = 0; x < img.cols; x++)
{
double y = k * x + b;
if (round(y) >= 0 && round(y) <= img.rows)
{
circle(img, Point(x, y), 1, Blue, 1, 8);
}
}
}
int main()
{
Mat img = imread("test.jpg");
Mat gray;
cvtColor(img,gray,CV_BGR2GRAY);
GaussianBlur(gray,gray,Size(5,5),1.6);
namedWindow("img",CV_WINDOW_NORMAL);
namedWindow("gray", CV_WINDOW_NORMAL);
//threshold(img,img,80,255,CV_THRESH_BINARY);
//adaptiveThreshold(gray,gray,255, ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY,3,3);
Canny(gray,gray,100,200);//canny参数
double MaxR = sqrt(gray.cols*gray.cols+gray.rows*gray.rows);//距离范围应该为[0,√]
double MaxDegree = 180;//角度范围为0-180
double interVal = 0.5;//间距为0.5
//floor向下取整,round向上取整
vector<vector<int>>A(round(MaxR),vector<int>(MaxDegree));
uchar *data =gray.data;
int step =gray.step[0];
for (int x = 0; x< gray.cols;x++)
{
for (int y = 0; y < gray.rows; y++)
{
if ((int)data[y*step+x] == 255)
{//遍历所有存在的点
for (double degree =0; degree <MaxDegree; degree++)
{//遍历所有斜率k的取值
double dis = x*cos((Pi*degree)/180.0)+y*sin((Pi*degree) / 180.0);
if (dis >= MaxR || dis <0)continue;
A[round(dis)][degree]++;
}
}
}
}
cout << "test1" << endl;
//寻找累加器矩阵中的极值点
int max = 0,degree = 0, dis = 0;
for (int i = 0; i < A.size(); i++)
{
for (int j = 0; j < A[0].size(); j++)
{
if (A[i][j] > max) { max = A[i][j]; dis= i; degree = j; }
}
}
//cout << "test2" << endl;
cout << "找到的角度" << degree << endl;
cout << "距离:" << dis << endl;
double k = -cos((Pi*degree) / 180.0) / sin((Pi*degree) / 180.0);//若用tan,考虑什么时候为0
double b = dis / sin((Pi*degree) / 180.0);
cout << "斜率:" << k << endl;
cout << "截距:" << b << endl;
//画出找到的曲线
paint(img, k,b);
imshow("img",img);
imshow("gray",gray);
waitKey(0);
destroyAllWindows();
return 0;
}
需要完善的地方很多!例如求八邻域极值,避免重复检测等,后期还需要了解如何实现霍夫圆检测。