Sobel算子和梯度计算
一、目的与原理
(1)目的:Sobel算子主要用于边缘检测,对噪声平滑抑制。
(2)原理:图像梯度用于边缘检测。边缘是像素值发生跃迁的地方,是图像的显著特征之一。图像中有灰度值的变化就会有梯度,从而产生边缘,在边缘处,具有变化的强弱及方向。图像上可以使用一阶差分来计算相邻像素之间的变化率,我们利用卷积和特定的算子来计算相邻像素的变化率。sobel算子可以计算相邻三个点之间的变化率。它们用于一阶算子的边缘检测,利用像素点上下、左右相邻点的灰度差求取边缘。计算水平梯度,检测垂直边缘 ,计算垂直梯度,检测水平边缘,最后通过将水平边缘图和垂直边缘图相加可近似得总边缘图。
原信号与求导数之间的变化关系:
下图中灰度值的“跃升”表示边缘的存在:
使用一阶微分求导我们可以更加清晰的看到边缘“跃升”的存在(这里显示为局部极大值):
梯度理论知识:
图像是离散函数,在某点的梯度可以用向前差商、向后差商或者中心差商获得。这里采用中心差商可以获取图像某点的导数值。
由于除法会导致低阶的重要字节丢失, 所以我们把向量乘与一个数将分母忽略掉,分子写成一维卷积的形式相当于[1,0,-1]。Sobel模板之所以能够较好的抑制噪声效果,
有平滑的效果的原因是将中间行的系数加大,即Sobel卷积模板一般如下:
计算图像梯度值的步骤:
1. 分别与图像进行卷积
2. 把得到的值写在一起得到图像梯度向量
其中梯度方向为:
梯度幅度:
两个结果求出近似 梯度:
也可以用下面更简单公式代替:
二、步骤
方法一:
(1)将图片转化为灰度图
(2)通过x方向的卷积核计算出X方向的灰度值
(3)通过y方向的卷积核计算出y方向的灰度值
(4)x方向的y方向的灰度图叠加成为整体边缘
方法二:
(1)将图片转化为灰度图
(2)根据卷积核权重对像素点的灰度值卷积
(3) 对卷积结果平方根处理
(4) 对平方根结果进行截断处理
(5) 将最终结果赋值到输出图像中
(6) 显示图像
三、伪代码
方法一:
输入:原图src
输出:结果图dst
void SobelXY(Mat src, Mat &dst)
{
If(判断src是否为灰度图)
不是灰度图则转为灰度图
根据卷积核权重对像素点的灰度值卷积,然后相乘(平方)
对上一步的平方结果开根号
对上一步开根号处理结果进行截断处理
将结果赋值到输出图像
}
方法二:
输入:原图src
输出:X方向的结果图dst
void SobelX (Mat src, Mat &dst)
{
If(判断src是否为灰度图)
不是灰度图则转为灰度图
根据卷积核权重对像素点的灰度值卷积
截断处理
将结果赋值到输出图像,得到X方向的结果图
}
输入:原图src
输出:Y方向的结果图dst
void SobelY (Mat src, Mat &dst)
{
If(判断src是否为灰度图)
不是灰度图则转为灰度图
根据卷积核权重对像素点的灰度值卷积
截断处理
将结果赋值到输出图像,得到Y方向的结果图
}
四、特点
优点:索贝尔算子不但产生较好的边缘检测效果,而且对噪声具有平滑抑制作用
缺点:得到的边缘较粗,且可能出现伪边缘。
五、源码
void SobelXY(Mat src, Mat &dst)
{
int temp=0;
for (int i = 1; i < src.rows - 1; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
temp = sqrt((src.data[(i - 1)*src.step + j + 1]
+ 2 * src.data[i*src.step + j + 1]
+ src.data[(i + 1)*src.step + j + 1]
- src.data[(i - 1)*src.step + j - 1]
- 2 * src.data[i*src.step + j - 1]
- src.data[(i + 1)*src.step + j - 1])*(src.data[(i - 1)*src.step + j + 1]
+ 2 * src.data[i*src.step + j + 1] + src.data[(i + 1)*src.step + j + 1]
- src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
- src.data[(i + 1)*src.step + j - 1]) + (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
+ src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- 2 * src.data[(i + 1)*src.step + j]
- src.data[(i + 1)*src.step + j + 1])* (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
+ src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- 2 * src.data[(i + 1)*src.step + j]
- src.data[(i + 1)*src.step + j + 1]));
if (temp < 0)
temp = 0;
if (temp > 255)
temp = 255;
dst.data[i*dst.step + j] = temp;
}
}
}
void SobelX(Mat src, Mat &dst)
{
int temp=0;
for (int i = 1; i < src.rows - 1; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
temp =
+src.data[(i - 1)*src.step + j - 1]
+ 2 * src.data[i*src.step + j - 1]
+ src.data[(i + 1)*src.step + j - 1]
- src.data[(i - 1)*src.step + j + 1]
- 2 * src.data[i*src.step + j + 1]
- src.data[(i + 1)*src.step + j + 1];
if (temp < 0)
temp = 0;
if (temp > 255)
temp = 255;
dst.data[i*dst.step + j] = abs(temp);
}
}
}
void SobelY(Mat src,Mat& dst)
{
int temp = 0;
for (int i = 1; i < src.rows - 1; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
temp
= src.data[(i - 1)*src.step + j - 1]
+ 2 * src.data[(i - 1)*src.step + j]
+ src.data[(i - 1)*src.step + j + 1]
- src.data[(i + 1)*src.step + j - 1]
- 2 * src.data[(i + 1)*src.step + j]
- src.data[(i + 1)*src.step + j + 1];
if (temp < 0)
temp = 0;
if (temp > 255)
temp = 255;
dst.data[i*dst.step + j] = abs(temp);
}
}
}
六、结果图
X+Y方式结果图:
平方开根号方式结果图: