1.sobel算子
概述:x方向和y方向的算子如下图所示。将x方向的算子放入图像进行开窗计算,容易想象,在图像的边界区域,计算出的值绝对值较大,这样可以计算出垂直的边界;同样用Gy进行开窗计算,可以计算出水平边界。通过融合,我们可以获得图像的边界信息。
函数:cv::Sobel(cv::InputArray src, cv::OutputArray dst, int ddepth, int dx, int dy, int ksize = 3)
参数:还有一些边界填充的参数不做介绍。
cv::InputArray src | 输入图像 |
cv::OutputArray dst | 输出图像 |
int ddepth | 图像深度,默认为-1 |
int dx | 需要计算垂直边界置1,否则置0 |
int dy | 需要计算水平边界置1,否则置0 |
int ksize = 3 | 核大小 |
代码:
#include <iostream>
#include <opencv.hpp>
#include <core/core.hpp>
#include <highgui/highgui.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat image_1 = imread("lena.jpg");
Mat sobel_x;
Mat sobel_y;
Mat res;
//这里使用的opencv环境是4.0.1,sobel运算之后好像会自动取绝对值,
//不需要再使用convertScaleAbs()函数对结果取绝对值
Sobel(image_1, sobel_x, CV_8U, 1, 0);
Sobel(image_1, sobel_y, CV_8U, 0, 1);
//对x,y分别做sobel运算然后加权相加,比用一个sobel运算计算边缘效果要更好一些
addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0,res);
imshow("lena", image_1);
imshow("x", sobel_x);
imshow("y", sobel_y);
imshow("res",res);
waitKey(0);
return 0;
}
最终结果:
2.scharr算子
概述:scharr算子与sobel算子唯一的区别是算子矩阵的值不同,scharr算子意味着相邻点对于结果有更大的影响力。
3.laplacian算子
概述:laplacian算子的矩阵结构与sobel算子完全不同,将sobel算子的矩阵对图像进行开窗运算大家可以非常容易的想象在边界处会产生的结果,而laplacian算子又是怎么来的呐?他是如何计算出图像的边界的呐?
对于一个连续函数的求导,我们可以遵循以下求导公式:
在图像中,对于像素点o的梯度计算公式可以简单看作((x+1)(x-1)表示x轴相邻格子的像素值):
在平滑的区域,梯度很小。所以不难想象,边缘区域的梯度值是极值点。
在连续函数中,对极值点求导,结果为零,引申到离散图像中,对图像边缘区域梯度求导,也就是对图像的极值点求导,结果也应零。而梯度是对图像的一阶导数,对梯度求导,也就是图像的二阶导数。换句话说图像 的二阶导数的结果为零,代表这是图像的边缘。
将连续函数一阶导数和二阶导数引申到离散图像中,然后拓展到2维图像,我们可以得到:
当上述二阶导数为零时,即使图像的边缘。将其转化为矩阵,即laplacian算子:
函数:cv::Laplacian(cv::InputArray src, cv::OutputArray dst, int ddepth, int ksize = 1)
参数:
cv::InputArray src | 输入图像 |
cv::OutputArray dst | 输出图像 |
int ddepth | 图像深度,默认为-1 |
int ksize | ksize=1,核大小3*3 |
代码:
#include <iostream>
#include <opencv.hpp>
#include <core/core.hpp>
#include <highgui/highgui.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat image_1 = imread("lena.jpg");
Mat res;
Laplacian(image_1, res, CV_8U, 1);
imshow("lena", image_1);
imshow("res",res);
waitKey(0);
return 0;
}
最终结果: