# 前置处理
注意:这不是教程,需要你已经对霍夫变换有详细的了解
使用Hough变换时需要输入一个图像边缘检测后二值化的图像,可以调用openCV的canny函数获得,canny函数的实现将会在之后的文章中写入,openCV中canny函数的使用详情请查阅OpenCV: Feature Detection
此处使用下图
# 代码实现
## 环境、头文件、常量定义
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp> //这两个请根据自己的路径选择
#include <cmath>
#include <vector>
#include <map>
using namespace cv;
using namespace std;
using TYPE = uchar;
const double PI = 3.1415926;
const double HPI = PI / 2;
## 函数头参数说明
//返回三维数组,位置含义分别是(角度,该角度下所有的直线,直线中所有点的坐标(行,列))
vector<vector<vector<pair<int, int>>>> hough(Mat& picture, double step, int lim1, int lim2);
//picture:图像
//step:从-pi/2开始到pi/2中,每一次的角度增量
//lim1:每条线最短的长度
//lim2:容许线段断开的最小像素
## Hough变换主体函数
vector<vector<vector<pair<int, int>>>> hough(Mat& picture, double step, int lim1, int lim2)
{
int rows = picture.rows;
int cols = picture.cols;
double theta = -HPI; //当前角度
int len = round(PI / step);//总共的步数
double total = 0;
//统计有效点总数
for_each(picture.begin<TYPE>(), picture.end<TYPE>(),[&total](TYPE var) {if (var != 0) {++total;}});
vector <pair<int,int>> position(total);//有效点的所有位置
vector<vector<vector<pair<int, int>>>>result(len);//结果
long long i = 0;
for (int row = 0; row < rows; ++row)//保存每个有效点的坐标
{
for (int col = 0; col < cols; ++col)
{
if (picture.at<TYPE>(row, col) != 0)
{
position[i] = pair<int, int>(row, col);
++i;
}
}
}
long long rho = 0;
for (int i =0;i<len;++i)//对每个角度
{
total = 0;
map<double, vector<pair<int, int>>> rho_to_pos;//计算出的rho和坐标的关联容器
for (pair<int,int> coordinate :position)//对有效的每个点,计算参数空间的所有取值
{
rho = round(coordinate.first * cos(theta) + coordinate.second * sin(theta));//计算此点的rho
//rho = rho - rho % 4;//可选,模一个数可以舍入到最近的这个数的倍数
rho_to_pos[rho].push_back(coordinate);
}
result[i].resize(rho_to_pos.size());//初始化该角度的每个rho
int len = 0;
for (pair<double, vector<pair<int, int>>> val: rho_to_pos)//返回符合数目要求的直线
{
int num = val.second.size();//该支线点的总数目
if (num>lim2)//大于总数目则保存
{
result[i][len]=move(val.second);//转移资源
++len;
}
}
result[i].resize(len);//调整长度
theta += step;//增加角度
}
return shear(result,lim1, lim2);
}
## 直线剪切函数和距离判断函数
vector<vector<vector<pair<int, int>>>> shear(vector<vector<vector<pair<int, int>>>>& thetas, int lim1,int lim2)
{
int thetas_size = thetas.size();
vector<vector<vector<pair<int, int>>>>result(thetas_size);//结果
for (int i = 0; i < thetas_size;++i)//对每个角度
{
for (auto& line : thetas[i])//每个角度的每条线
{
int line_len = line.size();//线长
int total = 1;
bool cutoff = false;//受否分割
int begin = 0;
int end = 0;
for (int j = 1; j <line_len;++j)//对线进行切割
{
if (dis(line[j - 1], line[j]) < lim1)//小于范围,则增加线长
{
++total;
}
else//大于范围,分割
{
if (total>lim2)//大于范围且大于要求长度
{
end = j - 1;
cutoff = true;//切割
}
else//大于范围但不满足长度要求
{
total = 1;//长度置1
begin = j;//重置起点
}
}
if (j == line_len - 1)//到末尾,分割
{
end = j;//标记结束位置
cutoff = true;//切割
}
if (total > lim2 and cutoff == true)//大于线长,分割
{
result[i].push_back(vector<pair<int, int>>(line.begin() + begin, line.begin() + end));
cutoff = false;
begin = j;//标记新起点
total = 1;//线长归1
}
}
}
}
return result;
}
int dis(pair<int, int> &p1, pair<int, int> &p2)//街区距离
{
return abs(p1.first - p2.first) + abs(p1.second - p2.second);
}
# 检测结果
int main()
{
Mat p = imread("9.png", 0);//读取一张二值图像
Mat p_line = imread("8.bmp", 0);//在此图像上绘图
vector<vector<vector<pair<int, int>>>> lines = hough(p, PI / 360,8,20);//变换
//绘图
for (auto&theta:lines)//对每个角度
{
for (auto& every_line : theta)//每个角度的每条线
{
pair<int, int> f = every_line[0];
pair<int, int> l = every_line[every_line.size()-1];//每个角度的每条线的第一个和最后一个点
Point pt1, pt2;
pt1.x = f.second;//注意x对应的是列
pt1.y = f.first;//注意y对应的是行
pt2.x = l.second;
pt2.y = l.first;
line(p_line, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);//在图像上绘图
}
}
imwrite("bk_line.png", p_line);
waitKey(0);
return 0;
}
耗时:Release下10s,图像大小:2076*2816 ,黑色边缘为检测到的直线。