基本概念
1.算子
算子也就是滤波器,或者又称作卷积核,通常是一个3x3或者8x8的矩阵,在数字图像处理中有广泛的应用,将滤波器用来对二维图像中的每个像素做点积操作,及对应的像素点相乘再求和,可以达到边缘提取,图像分割等各种效果
2.图像的梯度
在二维图像中,边缘就是图像的像素值发生突变的那些点的集合,边缘的像素点与周围领域的像素点在亮度上存在较大差异,在高等数学中梯度代表了函数在某个点上最大的方向导数,也就是在沿着该方向函数的变化最快,那么在图像中,二维函数的梯度就可以理解为沿着x轴或者y轴的偏导数,由于二维图像是一个离散函数,所以我们这里需要采用偏导数的差分形式
因此,Sobel算子是一个一阶微分算子,图像的梯度可以通过相邻像素值的差分得到,所以也就不难理解Sobel算子的滤波器结构
上图是x方向上的,y方向上的很类似
后续通过求该梯度的模,开平方根,或者通过直接想加得到一个灰度的近似值
下面我们可以自己动手通过C++来实现一个Sobel算子的边缘检测
代码如下
cv::Mat Sobel(cv::Mat img) {
int width = img.cols;
int height = img.rows;
// 存储x方向的sobel算子结果
cv::Mat gx = cv::Mat::zeros(height, width, CV_8UC1);
// 存储y方向的sobel算子结果
cv::Mat gy = cv::Mat::zeros(height, width, CV_8UC1);
for(int i = 1 ; i < height - 1; i ++) {
for(int j = 1; j < width - 1; j ++) {
// calculate sobel for x
gx.at<uchar>(i,j) = img.at<uchar>(i-1,j-1) * (-1) +
img.at<uchar>(i-1,j) * 0 +
img.at<uchar>(i-1,j+1) * 1 +
img.at<uchar>(i,j-1) * (-2) +
img.at<uchar>(i,j) * 0 +
img.at<uchar>(i,j+1) * 2 +
img.at<uchar>(i+1,j-1) * (-1) +
img.at<uchar>(i+1, j) * 0 +
img.at<uchar>(i+1, j+1) * 1;
// calculate sobel for y
gy.at<uchar>(i,j) = img.at<uchar>(i-1,j-1) * (-1) +
img.at<uchar>(i-1,j) * 0 +
img.at<uchar>(i-1,j+1) * 1 +
img.at<uchar>(i,j-1) * (-2) +
img.at<uchar>(i,j) * 0 +
img.at<uchar>(i,j+1) * 2 +
img.at<uchar>(i+1,j-1) * (-1) +
img.at<uchar>(i+1, j) * 0 +
img.at<uchar>(i+1, j+1) * 1;
}
}
cv::Mat result = cv::Mat::zeros(height, width, CV_8UC1);
// 两个方向上的梯度计算结果叠加
addWeighted(gx, 1, gy, 1, 0, result);
return result;
}
sobel算子的应用结果为(下面贴一张自己的帅照):
由于部分噪声可能对Sobel算子的结果有影响,所以对原图先做了一次高斯模糊,然后转换成灰度图,再应用了Sobel算子。
Sobel对部分图像较弱的边缘可能检测效果教差,OpenCV自带的API cv::Sobel函数中,还可以传入Scharr算子,Scharr是对Sobel算子差异性的增强,其滤波器结构如下