Sobel算子的理解

Sobel算子的理解sobel算子主要用于获得数字图像的一阶梯度,常见的应用和物理意义是边缘检测。原理算子使用两个33的矩阵(图1)算子使用两个33的矩阵(图1)去和原始图片作卷积,分别得到横向G(x)和纵向G(y)的梯度值,如果梯度值大于某一个阈值,则认为该点为边缘点Gx方向的相关模板:Gy方向的相关模板:看了网上的很多资料,都把卷积和相关的概念给弄糊了,书上

大家好,又见面了,我是你们的朋友全栈君。

sobel算子主要用于获得数字图像的一阶梯度,常见的应用和物理意义是边缘检测。

原理

算子使用两个33的矩阵(图1)算子使用两个33的矩阵(图1)去和原始图片作卷积,分别得到横向G(x)和纵向G(y)的梯度值,如果梯度值大于某一个阈值,则认为该点为边缘点

Gx方向的相关模板:

Sobel算子的理解

Gy方向的相关模板:

这里写图片描述

看了网上的很多资料,都把卷积和相关的概念给弄糊了,书上给的Sobel的模板不是卷积模板,而是相关模板,因为卷积的话要先将模板旋转180度以后再与图像做相关的操作。

所以Sobel的卷积模板是:

Gx=

1

0

-1

 2

0

-2

1

0

-1

Sobel算子的理解

具体计算如下:

图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:

Sobel算子的理解

通常,为了提高效率使用不开平方的近似值:

Sobel算子的理解

然后可用以下公式计算梯度方向:

Sobel算子的理解

opencv还提供了一个scharr函数,比Sobel算子更为精准,也是3×3的模板。

这里写图片描述

下面放上源代码:

#include "opencv2/imgproc/imgproc.hpp"  #include "opencv2/highgui/highgui.hpp"  #include <iostream>  #include <cmath>using namespace cv;using namespace std;bool sobelEdge(Mat&  srcImage, Mat& resultImageX, Mat& resultImageY, uchar threshold){	CV_Assert(srcImage.channels() == 1);	// 初始化水平核因子	Mat sobelx = (Mat_<double>(3, 3) << -1, 0,		1, -2, 0, 2, -1, 0, 1);	// 初始化垂直核因子	Mat sobely = (Mat_<double>(3, 3) << -1, -2, -1,		0, 0, 0, 1, 2, 1);	resultImageX = Mat::zeros(srcImage.rows - 2,		srcImage.cols - 2, srcImage.type());	resultImageY = Mat::zeros(srcImage.rows - 2,		srcImage.cols - 2, srcImage.type());	double edgeX = 0;	double edgeY = 0;	double graMagX = 0;// 垂直方向上的梯度模长	double graMagY = 0;// 水平方向上的梯度模长	for (int k = 1; k < srcImage.rows - 1; ++k) 	{		for (int n = 1; n < srcImage.cols - 1; ++n) 		{			edgeX = 0;			edgeY = 0;			// 遍历计算水平与垂直梯度			for (int i = -1; i <= 1; ++i) 			{				for (int j = -1; j <= 1; ++j)				{					edgeX += srcImage.at<uchar>(k + i, n + j) *						sobelx.at<double>(1 + i, 1 + j);					edgeY += srcImage.at<uchar>(k + i, n + j) *						sobely.at<double>(1 + i, 1 + j);				}			}			// 计算垂直方向上的梯度模长			graMagX = sqrt(pow(edgeX, 2));			// 计算水平方向上的梯度模长			graMagY = sqrt(pow(edgeY, 2));			// 二值化			resultImageX.at<uchar>(k - 1, n - 1) =				((graMagX > threshold) ? 255 : 0);			// 二值化			resultImageY.at<uchar>(k - 1, n - 1) =				((graMagY > threshold) ? 255 : 0);		}	}	return true;}int OTSU(Mat &srcImage){	int nRows = srcImage.rows;	int nCols = srcImage.cols;	int threshold = 0;	double max = 0.0;	double AvePix[256];	int nSumPix[256];	double nProDis[256];	double nSumProDis[256];						 	for (int i = 0; i < 256; i++)	{		AvePix[i] = 0.0;		nSumPix[i] = 0;		nProDis[i] = 0.0;		nSumProDis[i] = 0.0;	}		for (int i = 0; i < nRows; i++)	{		for (int j = 0; j < nCols; j++)		{			nSumPix[(int)srcImage.at<uchar>(i, j)]++;		}	}		for (int i = 0; i < 256; i++)	{		nProDis[i] = (double)nSumPix[i] / (nRows*nCols);	}		AvePix[0] = 0;	nSumProDis[0] = nProDis[0];		for (int i = 1; i < 256; i++)	{		nSumProDis[i] = nSumProDis[i - 1] + nProDis[i];		AvePix[i] = AvePix[i - 1] + i*nProDis[i];	}	double mean = AvePix[255];	for (int k = 1; k < 256; k++)	{		double PA = nSumProDis[k];		double PB = 1 - nSumProDis[k];		double value = 0.0;		if (fabs(PA) > 0.001 && fabs(PB) > 0.001)		{			double MA = AvePix[k];//前一半的平均			double MB = (mean - PA*MA) / PB;//后一半的平均			value = value = (double)(PA * PB * pow((MA - MB), 2));//类间方差  		         //或者这样value = (double)(PA * PB * pow((MA-MB),2));//类间方差			 //pow(PA,1)* pow((MA - mean),2) + pow(PB,1)* pow((MB - mean),2)			if (value > max)			{				max = value;				threshold = k;			}		}	}	return threshold;}int main(){	Mat srcImage = cv::imread("building.jpg");	if (!srcImage.data)		return -1;	Mat srcGray;	cvtColor(srcImage, srcGray, CV_BGR2GRAY);	imshow("srcGray", srcGray);	//调用二值化函数得到最佳阈值	int otsuThreshold = OTSU(srcGray);	cout << otsuThreshold << endl;	Mat XresultImage;	Mat YresultImage;	sobelEdge(srcGray, XresultImage, YresultImage, otsuThreshold);	Mat resultImage;	//水平垂直边缘叠加	addWeighted(XresultImage, 0.5, YresultImage, 0.5, 0.0, resultImage);	imshow("resx", XresultImage);	imshow("resy", YresultImage);	imshow("res", resultImage);	waitKey(0);	return 0;}

原图灰度图:

Sobel算子的理解

垂直边缘图像:

Sobel算子的理解

水平边缘图像:

Sobel算子的理解

水平垂直合一起:

Sobel算子的理解

从上图可以看出 边缘图还有很多不是边缘点的也被取出来了,下一步就是做非极大值抑制

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/158971.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • 【实用软件】局域网传输神器-LANDrop[通俗易懂]

    【实用软件】局域网传输神器-LANDrop[通俗易懂]软件介绍LANDrop是一款开源免费的支持跨平台的「局域网文件传输工具」 它的使用体验上可以媲美苹果生态的“隔空投送”功能! 能超级快速方便地将各种设备上的照片、视频、文档、文件发送到别的设备去软件功能LANDrop完全依靠局域网WIFI进行无线传输,速度极快 而且这款软件完全免费,并不限制任何平台 即便发送体积巨大的视频文件也完全没有问题,比起使用微信、QQ、网盘更加方便,速度更快,也不必担心图片/视频画质被压缩的烦恼下载地址下载地址https://url37.ctfile

    2022年5月4日
  • 启动马达接线实物图_软启动器怎么接线?一张电路图一张实物图供大家参考

    启动马达接线实物图_软启动器怎么接线?一张电路图一张实物图供大家参考朋友们大家好,我是大俵哥,今天我们来聊一下软启动。很多大型动力设备在启动的时候,启动电流都是比较大的,对整个电网有冲击性,所以不能直接启动,具体原因有以下两点。一,有的电机启动电流为额定电流的4--7倍,直接启动会影响同一电网内的其他用电设备。二,直接启动产生较高的峰值转矩,不仅对驱动电机有冲击性,而且易损坏机械装置。软启动相比星三角降压启动、自耦变压器启动等效果要好一些,启动更加平稳,保护也更加…

    2022年6月6日
  • Android layout_Android源码

    Android layout_Android源码LayoutParams源码分析LayoutParams是布局参数的意思,我们在XML布局文件里的layout_xxx等属性都是对LayoutParams的描述。LayoutParams不属于View,是ViewGroup控制View的具体显示在哪里。

    2022年9月21日
  • S3C2440 之SPI

    S3C2440 之SPI概述:S3C2440有两个串行外设SPI接口,SPI具有全双工通信SPI方框图 SPI操作:通过使用SPI接口,S3C2440可以与外部器件同时发送、接收8位数据。当SPI接口为主机时,可以通过设置SPPREn寄存器来设置发送频率,当SPI为从机时,由其它主机提供时钟频率。当程序员写字节数据到SPTDATn寄存器,将同时开始发送和接受,在一些情况下,应该在写字节数据到SPT

    2022年5月2日
  • windows dumpbin工具[通俗易懂]

    windows dumpbin工具[通俗易懂]一般情况下,我们需要查看一个DLL或EXE中的包含的函数或是依赖的函数之类的信息,可以使用VS自带的工具dumpbin;  可以直接在命令行下输入dumpbin就可以查看他的使用说明,如果未显示,可以先运行VS安装目录下的vcvarsall.bat来设置一下环境变量就可以了 [cpp] viewplaincopyMicro

    2022年6月19日
  • Database(Mysql)发版控制二

    Database(Mysql)发版控制二

    2022年1月27日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号