本文共 3934 字,大约阅读时间需要 13 分钟。
Sobel算子是一种广泛应用于图像处理领域的边缘检测算子。它通过计算图像在水平和垂直方向上的梯度,能够有效地提取图像的边缘信息。与Prewitt算子和Roberts算子相比,Sobel算子的特点是其对像素位置的加权处理,能够在保持精度的同时,提高边缘检测的准确性。
Sobel算子的主要优势体现在以下几个方面:
Sobel算子的平滑过程采用了非归一化的高斯平滑原理。具体来说,平滑算子的系数来源于二项式展开式的系数。对于窗口大小为3的情况,平滑算子的系数为:[ [1, 1] ]而对于更大的窗口大小,平滑算子的系数可以通过将二项式系数进行扩展得到。例如,窗口大小为5时,平滑算子的系数为:[ [1, 3, 3, 1] ]
差分算子是通过对平滑算子进行后向差分得到的。例如,窗口大小为5时,差分算子的系数为:[ [1, 2, 0, -2, -1] ]差分算子的作用是从平滑算子中提取出边缘的变化信息。
Sobel算子的边缘检测流程可以分为两个主要步骤:
通过上述流程,Sobel算子能够分别计算出图像的水平和垂直边缘信息。
int factorial(int n) { int fac = 1; if (n == 0) return fac; for (int i = 1; i <= n; ++i) { fac *= i; } return fac;}cv::Mat getSobelSmooth(int wsize) { int n = wsize - 1; cv::Mat smoothKernel = cv::Mat::zeros(cv::Size(wsize, 1), CV_32FC1); for (int k = 0; k <= n; ++k) { float *ptr = smoothKernel.ptr(0); ptr[k] = factorial(n) / (factorial(k) * factorial(n - k)); } return smoothKernel;}cv::Mat getSobeldiff(int wsize) { cv::Mat diffKernel = cv::Mat::zeros(cv::Size(wsize, 1), CV_32FC1); cv::Mat smoothKernel = getSobelSmooth(wsize - 1); for (int k = 0; k < wsize; ++k) { if (k == 0) { diffKernel.at (0, k) = 1; } else if (k == wsize - 1) { diffKernel.at (0, k) = -1; } else { diffKernel.at (0, k) = smoothKernel.at (0, k) - smoothKernel.at (0, k - 1); } } return diffKernel;}
void sepConv2D_Y_X(cv::Mat &src, cv::Mat &dst, cv::Mat &kernel_Y, cv::Mat &kernel_X, int ddepth, cv::Point anchor = cv::Point(-1, -1), int delta = 0, int borderType = cv::BORDER_DEFAULT) { cv::Mat temp = conv2D(src, temp, kernel_Y, ddepth, anchor, delta, borderType); conv2D(temp, dst, kernel_X, ddepth, anchor, delta, borderType);}void sepConv2D_X_Y(cv::Mat &src, cv::Mat &dst, cv::Mat &kernel_X, cv::Mat &kernel_Y, int ddepth, cv::Point anchor = cv::Point(-1, -1), int delta = 0, int borderType = cv::BORDER_DEFAULT) { cv::Mat temp = conv2D(src, temp, kernel_X, ddepth, anchor, delta, borderType); conv2D(temp, dst, kernel_Y, ddepth, anchor, delta, borderType);}
void Sobel(cv::Mat &src, cv::Mat &dst_X, cv::Mat &dst_Y, cv::Mat &dst, int wsize, int ddepth) { cv::Mat smoothKernel = getSobelSmooth(wsize); cv::Mat diffKernel = getSobeldiff(wsize); sepConv2D_Y_X(src, dst_X, smoothKernel, diffKernel, ddepth); sepConv2D_X_Y(src, dst_Y, smoothKernel, diffKernel, ddepth); cv::abs(dst_X, dst_X); cv::abs(dst_Y, dst_Y); cv::convertScaleAbs(dst, dst);}
int main() { cv::Mat src = cv::imread("I:\\Learning-and-Practice\\2019Change\\Image process algorithm\\Img\\Fig1025(a)(building_original).tif"); if (src.empty()) { return -1; } if (src.channels() > 1) { cv::cvtColor(src, src, CV_RGB2GRAY); } int wsize = 3; cv::Mat SobelDiff, SobelSmooth; Sobel(src, dst_Y, dst_X, dst, wsize, CV_32FC1); cv::namedWindow("src", CV_WINDOW_NORMAL); imshow("src", src); cv::namedWindow("水平边缘", CV_WINDOW_NORMAL); imshow("水平边缘", dst_Y); cv::namedWindow("垂直边缘", CV_WINDOW_NORMAL); imshow("垂直边缘", dst_X); cv::namedWindow("边缘强度", CV_WINDOW_NORMAL); imshow("边缘强度", dst); cv::namedWindow("边缘强度取反-铅笔素描", CV_WINDOW_NORMAL); imshow("边缘强度取反-铅笔素描", 255 - dst); cv::waitKey(0); return 0;}
通过对比原图和边缘检测结果,可以清晰地看到Sobel算子捕捉到的边缘信息。图像的水平和垂直方向的边缘都被准确地提取出来,显示出图像的轮廓和细节。
将边缘检测结果取反后,可以得到一种类似铅笔素描的效果。这种效果特别适用于手绘风格的图像处理,能够突出图像的轮廓和主要细节。
转载地址:http://ggrfk.baihongyu.com/