openCV中霍夫变换的C++实现

本文介绍了如何使用Hough变换对二值化图像中的边缘进行检测,包括Hough变换的原理、参数设置以及在Canny函数辅助下,通过OpenCV实现的代码示例。展示了在给定图像上的直线检测结果和性能测试数据。
摘要由CSDN通过智能技术生成

# 前置处理

注意:这不是教程,需要你已经对霍夫变换有详细的了解

使用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 ,黑色边缘为检测到的直线。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值