sobel算子进行边缘检测

您所在的位置:网站首页 sobel算子边缘检测优缺点 sobel算子进行边缘检测

sobel算子进行边缘检测

2023-09-29 17:41| 来源: 网络整理| 查看: 265

使用sobel算子进行边缘检测的步骤如下:  1、高斯模糊,降噪。因为sobel算子对噪声比较敏感,因此要先对原图像进行高斯模糊,降噪2、将图像转换成灰度图像3、使用sobel函数,求x和y方向上的导数。4、将x方向的导数(边缘)和y方向的导数进行叠加。 (1)高斯模糊 //首先进行高斯模糊,降噪 Mat gauImage; GaussianBlur(srcImage, gauImage, Size(3, 3), 0, 0, 4);

原图如下:

 

(2)转换成灰度图 //将图片转换成灰度图 Mat grayImage; cvtColor(gauImage, grayImage, COLOR_BGR2GRAY); (3)使用sobel函数 //进行x和y方向的soble算子 Mat xGrand, yGrand; Sobel(grayImage, xGrand, CV_16S, 1, 0, 3); convertScaleAbs(xGrand, xGrand); Sobel(grayImage, yGrand, CV_16S, 0, 1, 3); convertScaleAbs(yGrand, yGrand);

 下面详细讲述一下x和y方向导数怎么求解。

对于一元连续函数的导数,我们直接通过公式求导就可以;对于多元连续函数的导数,我们需要求各个方向的偏导。但是对于存储在计算机中的图像来说,计算机只能够处理离散的像素值,不能够使用公式求解导数,因此只能够使用导数的定义来求解x和y方向的导数。比如求解x方向的导数:dx = I(i + 1) - I(i);求解y方向的导数:dx = I(j + 1) - I(j),某点的导数 = 该点x(y)方向的下一个像素值 - 该点的像素值,就可以得到该点x或者y方向的导数。

值得注意的是,因为sobel函数在求x或者y方向的导数时,使用的核是不一样的,得到的导数可能小于0,或者大于255,因此为了保护细节,最好选用16位(CV_16S)的输出图像的深度,并且调用convertScaleAbs(输出图像为8位),将所得结果尽可能的保护下来。

(4)将x和y方向的导数进行叠加

方法一:使用线性混合的方法进行叠加

//进行线性混合叠加 Mat dst; addWeighted(xGrand, 0.5, yGrand, 0.5, 0, dst, -1);

调用函数所需要的瞬间很短,得到的效果也很一般。最终的图片如下:

 

方法二:使用近似的方法,将x和y方向的导数进行相加

Mat dst = Mat :: zeros( xGrand.size(), yGrand.type()); //将x和y的梯度直接相加 int colsImage = srcImage.cols; int rowsImage = srcImage.rows; for (int row = 0; row < rowsImage; row++) { for (int col = 0; col < colsImage; col++) { int xValue = xGrand.at(row, col); int yValue = yGrand.at(row, col); dst.at(row, col) = saturate_cast(xValue + yValue); } }

这个方法消耗的时间较长,消耗了0.1s左右的时间,但是运行结果显而易见,获得了更多的细节和边缘。

 

方法三:根据勾股定理求出近似梯度

根据向量的加法,如果要求得图像整体的梯度,就要求x和y向量加法的模,也就是x和y构成的直角三角形的斜边。

具体代码如下:

//使用勾股定理获得更多的细节 int colsImage = srcImage.cols; int rowsImage = srcImage.rows; for (int row = 0; row < rowsImage; row++) { for (int col = 0; col < colsImage; col++) { uchar xValue = xGrand.at(row, col); uchar yValue = yGrand.at(row, col); dst.at(row, col) = saturate_cast(sqrt(xValue * xValue + yValue * yValue)); } }

这个方法消耗的时间比方法二消耗的时间略长一些,但是效果和方法二相差不多。因此,从时间方面考虑,完全可以使用第二种方法代替第三种方法。 

 

最终所有代码如下:

#include #include #include using namespace cv; int main(int argc, char ** argv) { Mat srcImage = imread("1.jpg"); if (srcImage.empty()) { printf("could not load this picture\n"); return -1; } imshow("原图像", srcImage); //首先进行高斯模糊,降噪 Mat gauImage; GaussianBlur(srcImage, gauImage, Size(3, 3), 0, 0, 4); //将图片转换成灰度图 Mat grayImage; cvtColor(gauImage, grayImage, COLOR_BGR2GRAY); imshow("灰度图", grayImage); //进行x和y方向的soble算子 Mat xGrand, yGrand; Sobel(grayImage, xGrand, CV_16S, 1, 0, 3); convertScaleAbs(xGrand, xGrand); Sobel(grayImage, yGrand, CV_16S, 0, 1, 3); convertScaleAbs(yGrand, yGrand); //进行叠加 float t1 = getTickCount(); Mat dst = Mat :: zeros( xGrand.size(), yGrand.type()); //使用线性混合的方法 //addWeighted(xGrand, 0.5, yGrand, 0.5, 0, dst, -1); 将x和y的梯度直接相加 //int colsImage = srcImage.cols; //int rowsImage = srcImage.rows; //for (int row = 0; row < rowsImage; row++) { // for (int col = 0; col < colsImage; col++) { // uchar xValue = xGrand.at(row, col); // uchar yValue = yGrand.at(row, col); // dst.at(row, col) = saturate_cast(xValue + yValue); // } //} //使用勾股定理获得更多的细节 int colsImage = srcImage.cols; int rowsImage = srcImage.rows; for (int row = 0; row < rowsImage; row++) { for (int col = 0; col < colsImage; col++) { uchar xValue = xGrand.at(row, col); uchar yValue = yGrand.at(row, col); dst.at(row, col) = saturate_cast(sqrt(xValue * xValue + yValue * yValue)); } } float timeConsume = (getTickCount() - t1) / getTickFrequency(); printf("线性混合叠加消耗的时间是:%f\n", timeConsume); imshow("最终图像", dst); imwrite("finalImage.jpg", dst); waitKey(0); return 0; }

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3