赞
踩
在OpenCV中,Sobel算法可以对图片中的值求一阶导数,从而计算出图片中的边缘线。其原理如下面的示意图:
那么,如果再求一次导数的,即求二阶导数,其实也可以找出这个颜色值显著变化的分界点:
可以看到,现在颜色值显著变化的位置,其导数值为0.
但是这有一个问题,就是二阶导数为0的也可以是一些无意义的值。所以,必须要进行一些过滤。
拉普拉斯算子的算法公式定义如下:
L
a
p
l
a
c
e
(
f
)
=
∂
2
f
∂
x
2
+
∂
2
f
∂
y
2
Laplace(f) = \frac{\partial^2f}{\partial x^2} + \frac{\partial^2f}{\partial y^2}
Laplace(f)=∂x2∂2f+∂y2∂2f
可以看到拉普拉斯算法可以同时对两个维度进行求导,这是它相对于Sobel算法的优势。但是由于拉普拉斯算法还是要求斜率,所以其内部仍然调用了Sobel算法。
在OpenCV中,使用Laplacian()
函数来进行拉普拉斯计算,其函数原型如下:
void cv::Laplacian( InputArray src, //输入图
OutputArray dst, //输出图
int ddepth, //输出的数据类型,-1表示与输入图一致
int ksize = 1, //卷积核尺寸,必须是正奇数
double scale =1, //计算结果的放大系数,默认为1,即不放大
double delta = 0, //计算结果的偏移值,默认为0,即不偏移
int borderType = BORDER_DEFAULT) //图像边缘的扩充方式,默认为镜像复制
- 当
ksize = 1
时,使用一个 3 × 3 3 \times 3 3×3的卷积核,如下:
[ 0 1 0 1 − 4 1 0 1 0 ]0101−41010 ⎡⎣⎢0101−41010⎤⎦⎥
在进行拉普拉斯求导之前也要进行滤波和灰度化,以去除噪音。
这里我们将拉普拉斯计算的结果中的数据类型定义为CV_16S
,是为了防止溢出。接着又通过convertScaleAbs()
函数转换回了CV_8U
类型。
完整代码如下:
#include <opencv2/imgproc.hpp> #include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> using namespace cv; int main() { Mat src{ imread("lena.jpg") }; //高斯滤波 Mat blured; GaussianBlur(src, blured, Size(3, 3), 0, 0, BORDER_DEFAULT); //灰度化 Mat gray; cvtColor(blured, gray, COLOR_BGR2GRAY); //拉普拉斯 Mat dst; Laplacian(gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT); //转换为CV_8U Mat abs_dst; convertScaleAbs(dst, abs_dst); imshow("原图", src); imshow("Laplace", abs_dst); waitKey(0); }
运行结果如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。