OpenCV-基本边缘检测算子Sobel实现

简要描述
sobel算子主要用于获得数字图像的一阶梯度,常见的应用是边缘检测。
原理
算子使用两个3*3的矩阵(图1)算子使用两个3*3的矩阵(图1)去和原始图片作卷积,分别得到横向G(x)和纵向G(y)的梯度值,如果梯度值大于某一个阈值,则认为该点为边缘点

                  sobel kernel
                           图1:卷积矩阵

        sobel convolved
                            图2:卷积运算  


图像直接卷积实现Sobel:

//图像直接卷积实现Sobel
bool sobelEdge(const cv::Mat image, cv::Mat &result, uchar threshold)
{
	CV_Assert(image.channels() == 1);
	//初始化水平核因子
	cv::Mat sobelx = (cv::Mat_<float>(3, 3) << 1, 0, -1, 2, 0, -2, 1, 0, -1);
	//初始化垂直核因子
	cv::Mat sobely = (cv::Mat_<float>(3, 3) << 1, 2, 1, 0, 0, 0, -1, -2, -1);
	result = cv::Mat::zeros(image.rows - 2, image.cols - 2, image.type());
	double graMag = 0;
	for (int i = 1; i < image.rows - 1; i++)
	{
		for (int j = 1; j < image.cols - 1; j++)
		{
			float edgex = 0, edgey = 0;
			//遍历计算水平与垂直梯度
			for (int k = -1; k < 2; k++)
			{
				for (int p = -1; p < 2; p++)
				{
					edgex += (float)image.at<uchar>(k + i, p + j) * sobelx.at<float>(1 + k, 1 + p);
					edgey += (float)image.at<uchar>(k + i, p + j) * sobely.at<float>(1 + k, 1 + p);
				}
			}
			//计算梯度模长
			graMag = sqrt(pow(edgex, 2) + pow(edgey, 2));
			//二值化
			result.at<uchar>(i - 1, j - 1) = ((graMag > threshold) ? 255 : 0);
		}
	}
	return true;
}


运行结果:



非极大值抑制Sobel边缘实现:

//非极大值抑制实现Sobel竖直细化边缘
bool SobelVerEdge(cv::Mat image, cv::Mat &result)
{
	CV_Assert(image.channels() == 1);
	image.convertTo(image, CV_32FC1);
	//水平方向的Sobel算子
	cv::Mat sobelx = (cv::Mat_<float>(3, 3) << -0.125, 0, 0.125, -0.25, 0, 0.25, -0.125, 0, 0.125);
	//卷积操作
	cv::Mat ConResMat;
	cv::filter2D(image, ConResMat, image.type(), sobelx);
	//计算梯度的幅度
	cv::Mat graMagMat;
	cv::multiply(ConResMat, ConResMat, graMagMat);
	//根据梯度幅度及参数设置阈值(mean获取各个通道的均值)
	int scaleVal = 4;
	double thresh = scaleVal * cv::mean(graMagMat).val[0];
	cv::Mat resulttempMat = cv::Mat::zeros(graMagMat.size(), graMagMat.type());
	float *pDataMag = (float*)graMagMat.data;
	float *pDataRes = (float*)resulttempMat.data;
	const int rows = ConResMat.rows, cols = ConResMat.cols;
	for (int i = 1; i != rows - 1; i++)
	{
		for (int j = 1; j != cols - 1; j++)
		{
			//计算该点梯度与水平或垂直梯度值的大小并比较结果
			bool b1 = (pDataMag[i * cols + j] > pDataMag[i * cols + j - 1]);
			bool b2 = (pDataMag[i * cols + j] > pDataMag[i * cols + j + 1]);
			bool b3 = (pDataMag[i * cols + j] > pDataMag[(i - 1) * cols + j]);
			bool b4 = (pDataMag[i * cols + j] > pDataMag[(i - 1) * cols + j]);
			//判断邻域梯度是否满足大于水平或垂直的条件
			//并根据自适应阈值参数进行二值化
			pDataRes[i * cols + j] = 255 * ((pDataMag[i * cols + j] > thresh) && ((b1 && b2) || (b3 && b4)));
		}
	}
	resulttempMat.convertTo(resulttempMat, CV_8UC1);
	result = resulttempMat.clone();
	return true;
}

运行结果:



在OpenCV中,sobel算子在C++中的函数原型如下: 

void Sobel(InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT ) 
InputArray src:输入的原图像,Mat类型 
OutputArray dst:输出的边缘检测结果图像,Mat型,大小与原图像相同。 
int ddepth:输出图像的深度,针对不同的输入图像,输出目标图像有不同的深度,具体组合如下: 
- 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F 
- 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F 
- 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F 
- 若src.depth() = CV_64F, 取ddepth = -1/CV_64F 
注:ddepth =-1时,代表输出图像与输入图像相同的深度。 
int dx:int类型dx,x 方向上的差分阶数,1或0 
int dy:int类型dy,y 方向上的差分阶数,1或0 
其中,dx=1,dy=0,表示计算X方向的导数,检测出的是垂直方向上的边缘;dx=0,dy=1,表示计算Y方向的导数,检测出的是水平方向上的边缘。 
int ksize:为进行边缘检测时的模板大小为ksize*ksize,取值为1、3、5和7,其中默认值为3。


代码实现:

int main()
{
	cv::Mat image = cv::imread("1.jpg", 0);
	if (image.empty()) return -1;
	cv::imshow("image", image);
	cv::Mat edgeMat, edgexMat, edgeyMat;
	//求x方向Sobel边缘
	cv::Sobel(image, edgexMat, CV_16S, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);
	//求y方向Sobel边缘
	cv::Sobel(image, edgeyMat, CV_16S, 0, 1, 3, 1, 0, cv::BORDER_DEFAULT);
	//线性变换,转换输入数组元素为8位无符号整形
	cv::convertScaleAbs(edgexMat, edgexMat);
	cv::convertScaleAbs(edgeyMat, edgeyMat);
	//x与y方向边缘叠加
	cv::addWeighted(edgexMat, 0.5, edgeyMat, 0.5, 0, edgeMat);
	cv::imshow("edgeMat", edgeMat);
	cv::waitKey(0);
	return 0;
}


运行结果:


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值