18-Sobel算子
Sobel算子
1、卷积应用-图像边缘提取;
2、相关API;
3、代码演示;
4、掌握使用Sobel算子计算图像梯度,提取图像轮廓边缘;
卷积应用-图像边缘提取
1、边缘:像素值发生跃迁的地方,是图像的显著特征之一,在图像特征提取,对象检测,模式识别等方面有重要的作用;
2、捕捉提取边缘方法:对图像求取一阶导数,图像上前一个像素点像素值与后一个像素值的差值即为一阶导数:delta = f(x) - f(x-1); delta越大,说明像素在x方向变化越大,边缘信号越强;
3、通过Sobel算子,卷积操作即可求取灰度图像的在X方向和Y方向的梯度图像;
4、Sobel算子是离散微分算子,用来计算图像灰度的近似梯度,Sobel算子功能集合高斯平滑,微分求导,又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方向和Y方向的梯度图像; Laplace算子为二阶微分算子;
Sobel算子
1、Sobel算子值:分为水平梯度Sobel算子,垂直梯度Sobel算子两个;合成后(近似合成可以直接用加法直接将水平梯度和垂直梯度相加,速度更快,减少计算量,如下图)得到最终图像梯度;
2、Sobel算子在kernel(卷积核)= 3时求导不是很准确,OpenCV使用改进的Sobel算子,算子值如下:(算子值变大了)
相关API
1、Sobel();
参数如下:
注意:输出图像一般比灰度图像大,输出图像深度depth通常取值CV_16S或CV_32F,dx,dy取1,代表一阶导数,ksize取3(常见的Sobel算子为3阶),scale表示放大倍数,一般取1,delta常量值一般取0,borderType表示边缘处理方式;
注:Output depth栏下:-1表示Input depth栏对应的位图深度值;
convertScaleAbs():
2、Scharr();
参数基本同上;
使用注意
- Sobel算子对噪声比较敏感,容易受到噪声影响,使用时应先进行高斯模糊降噪;
- 使用步骤:
①高斯平滑处理;
②转灰度图像;
③求X和Y方向;
④对求得的结果取绝对值处理(梯度有较大的负值,这些值也是边缘信息,需要保留)(convertScaleAbs()函数);
⑤使用addWeighted()函数进行混合,得到综合图像,称为振幅图像;或者直接操作X梯度图像像素点与Y梯度图像像素点相加; - 正确处理Sobel算子运算得到的数据(取正)才能保证图像的正确显示;
Scharr()
函数相比于Sobel()
函数对干扰噪声有更好的抵抗效果;
Code
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src, dst;
src = imread("C:\\Users\\hello\\Desktop\\17.jpg");
if (!src.data)
{
cout << "could not load the image" << endl;
return -1;
}
char INPUT_WIN[] = "input image";
char OUTPUT_WIN[] = "Sobel image";
namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);
//namedWindow(OUTPUT_WIN, CV_WINDOW_AUTOSIZE);
imshow(INPUT_WIN, src);
//Sobel算子运算
Mat g1,gray_src,sx,sy;
GaussianBlur(src, g1, Size(3, 3), 0, 0); //一般给 3 * 3 小模糊
cvtColor(g1, gray_src, CV_BGR2GRAY);
imshow("gray image", gray_src);
//Sobel算子不需要自己再定义了
//Mat kernel_x = (Mat_<int>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1); //水平方向Sobel算子
//Mat kernel_y = (Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1); //垂直方向Sobel算子
Sobel(gray_src, sx, CV_32F, 1, 0, 3); //x方向梯度为1,y方向梯度为0
Sobel(gray_src, sy, CV_32F, 0, 1, 3); //x方向梯度为0,y方向梯度为1
//Sobel算子运算后梯度结果并不一定是正数,可能是负数(负梯度);需要调用函数转换为正数,保证信息不遗漏
//负的绝对值也保留了边缘信息,为了不遗漏边缘信息,这个函数必须有!
convertScaleAbs(sx, sx);
convertScaleAbs(sy, sy);
imshow("x gray", sx);
imshow("y gray", sy);
//方法1:按权重合成
//Mat fin;
//addWeighted(sx, 0.5, sy, 0.5, 0, fin); //使用简化的混合方法,没有开平方
//imshow("x y 混合结果", fin);
//方法2:直接像素操作相加,相比于addWeight()效果更好
Mat xygrad = Mat(sx.size(), sx.type());
int width = xygrad.cols;
int height = xygrad.rows;
printf("type: %d\n", sx.type());
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
int xg = sx.at<uchar>(row, col);
int yg = sy.at<uchar>(row, col);
int xy = xg + yg;
xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy); //有被截断
}
}
imshow("Sobel Image", xygrad);
//Scharr() API使用
Mat xsch, ysch;
Scharr(gray_src, xsch, CV_16S, 1, 0); //X方向Sobel运算
Scharr(gray_src, ysch, CV_16S, 0, 1); //Y方向Sobel运算
convertScaleAbs(xsch, xsch);
convertScaleAbs(ysch, ysch);
Mat xysch = Mat(xsch.size(), xsch.type());
int schwidth = xysch.cols;
int schheight = xysch.rows;
for (int row = 0; row < schheight; row++)
{
for (int col = 0; col < schwidth; col++)
{
int x = xsch.at<uchar>(row, col);
int y = ysch.at<uchar>(row, col);
int xys = x + y;
xysch.at<uchar>(row, col) = saturate_cast<uchar>(xys);
}
}
imshow("scharr image", xysch);
waitKey(0);
return 0;
}
效果
SCharr()运算的效果比Sobel更好一点:(纹理更明显)