1.拉普拉斯(Laplacian)算子
1.1基础介绍
最简单的各向同性导数算子是拉普赖斯算子,其具有旋转不变性,对于两个变量的函数f ( x , y ) f(x,y)f (x ,y ),其定义为
▽ 2 f = ∂ 2 f ∂ x 2 + ∂ 2 f ∂ y 2 \triangledown^2f=\frac{\partial ^2f}{\partial x ^2} + \frac{\partial ^2f}{\partial y ^2}▽2 f =∂x 2 ∂2 f +∂y 2 ∂2 f ,以离散形式表示上述公式为:
<br>
x方向有:$\frac{\partial ^2f}{\partial x ^2} = f(x+1, y) + f(x-1, y) – 2f(x,y) = (f(x+1, y) -f(x,y)) – (f(x,y)-f(x-1,y))
‘ < b r / > ‘ y 方 向 有 y方向有’‘y 方向有\frac{\partial ^2f}{\partial y ^2} = f(x, y+1) + f(x, y-1) – 2f(x,y)$
由上面的公式,离散形式的拉普拉斯变换为:
▽ 2 f ( x , y ) = f ( x , y + 1 ) + f ( x , y − 1 ) + f ( x + 1 , y ) + f ( x − 1 , y ) − 4 f ( x , y ) \triangledown ^2f(x,y) = f(x, y+1) + f(x, y-1)+f(x+1, y) + f(x-1, y) – 4f(x,y)▽2 f (x ,y )=f (x ,y +1 )+f (x ,y −1 )+f (x +1 ,y )+f (x −1 ,y )−4 f (x ,y )
使用卷积核的形式可表示为:
[ 0 1 0 1 − 4 1 0 1 0 ] \begin{bmatrix} 0& 1 & 0\ 1& -4 & 1 \ 0 & 1 & 0 \end{bmatrix}⎣⎡0 1 0 1 −4 1 0 1 0 ⎦⎤
拉普拉斯算子是图像的二阶梯度算子,在灰度恒定区域其结果为0,在像素均匀变化区域其结果也为0,在像素变化速率变化较大区域其值也会变大,因此可将拉普拉斯算子应用到 图像锐化和 边缘检测。
应用到图像锐化时,应用拉普拉斯算子后的结果丢失了恒定像素区域的信息,因此将其与原图相加可求得锐化后的图像。 (负中心就减正中心就加)。
1.2OpenCV API
Laplacian
见OpenCV文档
void cv::Laplacian(
InputArray src,
OutputArray dst,
int ddepth,
int ksize = 1,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
src
原图dst
结果,与原图同shape
ddepth
Desired depth目标图像的深度ksize
卷积核的大小,正奇数scale
可选的计算拉普拉斯值时的缩放因子delta
可选的计算拉普拉斯值时需加的值borderType
像素外推方法,不支持BORDER_WRAP
1.3 示例
void laplacianOperator(cv::Mat &img)
{
int kernel_size = 1;
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
const char* window_name = "Laplace Demo";
cv::GaussianBlur(img, img, cv::Size(3, 3), 0, 0, cv::BORDER_DEFAULT);
cv::Mat img_gray;
cv::cvtColor(img, img_gray, cv::COLOR_BGR2GRAY);
cv::Mat dst;
cv::Laplacian(img_gray, dst, ddepth, kernel_size, scale, delta, cv::BORDER_DEFAULT);
cv::Mat dst_abs;
cv::convertScaleAbs(dst, dst_abs);
cv::Mat compound_img;
cv::Mat imageEnhance;
cv::Mat kernel = (cv::Mat_<float>(3, 3) << 0, 1, 0, 1, -4, 1, 0, 1, 0);
cv::filter2D(img_gray, imageEnhance, CV_8UC3, kernel);
cv::imshow("filter2D", imageEnhance);
cv::subtract(img_gray, imageEnhance, compound_img);
cv::imshow("Origin: ", img_gray);
cv::imshow(window_name, dst_abs);
cv::imshow("compound_img: ", compound_img);
cv::waitKey(0);
cv::imwrite("origin_image.png", img_gray);
cv::imwrite("laplacian.png", dst_abs);
cv::imwrite("enchanced_image.png", compound_img);
}
原图:
应用拉普拉斯算子后的二阶梯度图:
锐化后的图像:
2.Sobel算子
2.1基础
在进行边缘检测时需先求出图像的梯度,在像素的每个位置计算偏导数∂ f ∂ x \frac{\partial f}{\partial x}∂x ∂f 和∂ f ∂ y \frac{\partial f}{\partial y}∂y ∂f ,图像的梯度计算,通常使用前向或中心有限差分。使用前向差分计算: <br>
g x ( x , y ) = ∂ f ( x , y ) ∂ x = f ( x + 1 , y ) − f ( x , y ) g_x(x,y) = \frac{\partial f(x, y)}{\partial x} = f(x+1, y) – f(x, y)g x (x ,y )=∂x ∂f (x ,y )=f (x +1 ,y )−f (x ,y )
<br>
g y ( x , y ) = ∂ f ( x , y ) ∂ y = f ( x , y + 1 ) − f ( x , y ) g_y(x,y) = \frac{\partial f(x, y)}{\partial y} = f(x, y+1) – f(x, y)g y (x ,y )=∂y ∂f (x ,y )=f (x ,y +1 )−f (x ,y )
上述梯度的计算可以使用一维核对f ( x , y ) f(x,y)f (x ,y )滤波实现。
[ − 1 1 ] \begin{bmatrix} -1\ 1 \end{bmatrix}[−1 1 ]和[ 1 − 1 ] \begin{bmatrix} 1\ -1 \end{bmatrix}[1 −1 ]
感兴趣的区域是对角边缘时,需要使用二维核。Sobel核在中心系数上使用了权值2,
g x = ∂ f ∂ x = ( z 7 + 2 z 8 + z 9 ) − ( z 1 + 2 z 2 + z 3 ) g_x = \frac{\partial f}{\partial x} = (z_7 + 2z_8 + z_9) – (z_1+2z_2+z_3)g x =∂x ∂f =(z 7 +2 z 8 +z 9 )−(z 1 +2 z 2 +z 3 )和
g y = ∂ f ∂ y = ( z 3 + 2 z 6 + z 9 ) − ( z 1 + 2 z 4 + z 7 ) g_y = \frac{\partial f}{\partial y} = (z_3+2z_6+z_9)-(z_1+2z_4+z_7)g y =∂y ∂f =(z 3 +2 z 6 +z 9 )−(z 1 +2 z 4 +z 7 )
[ − 1 − 2 − 1 0 0 0 1 2 1 ] \begin{bmatrix} -1 & -2 & -1\ 0& 0 & 0\ 1& 2 & 1 \end{bmatrix}⎣⎡−1 0 1 −2 0 2 −1 0 1 ⎦⎤
和
[ − 1 0 1 − 2 0 2 − 1 0 1 ] \begin{bmatrix} -1 & 0 & 1\ -2& 0 & 2\ -1& 0 & 1 \end{bmatrix}⎣⎡−1 −2 −1 0 0 0 1 2 1 ⎦⎤
在中心位置使用2可以平滑图像,Sobel算子只为垂直边缘和水平边缘给出各向同性的结果。
Sobel算子在kernel大小为3时,使用上述算子计算梯度会产生不可忽略的误差,对图像中较弱的边缘提取效果较差。为了能够有效的提取出较弱的边缘,需要将像素值间的差距增大,因此引入Scharr算子。Scharr算子是对Sobel算子差异性的增强。
[ − 3 − 10 − 3 0 0 0 3 10 3 ] \begin{bmatrix} -3 & -10 & -3\ 0& 0 & 0\ 3& 10 & 3 \end{bmatrix}⎣⎡−3 0 3 −1 0 0 1 0 −3 0 3 ⎦⎤
和
[ − 3 0 3 − 10 0 10 − 3 0 3 ] \begin{bmatrix} -3 & 0 & 3\ -10& 0 & 10\ -3& 0 & 3 \end{bmatrix}⎣⎡−3 −1 0 −3 0 0 0 3 1 0 3 ⎦⎤
2.2 OpenCV API
void cv::Sobel (
InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
int ksize = 3,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
src
输入图像dst
输出图像,与src
同shape
ddepth
输出图像深度dx
x偏导的阶dy
y偏导的阶ksize
Sobel算子的大小
scale
可选的梯度值的缩放因子delta
可选的梯度值的偏置borderType
边界值的处理方式,dst
与src
同尺寸,故需对src
边界扩充,可用方式见BorderTypes,不支持BORDER_WRAP
2.3实例
void sobelOperator(cv::Mat &img)
{
cv::GaussianBlur(img, img, cv::Size(5, 5), 0, 0, cv::BORDER_DEFAULT);
cv::Mat img_gray;
cv::cvtColor(img, img_gray, cv::COLOR_BGR2GRAY);
cv::Mat grad, grad_x, grad_y, abs_grad_x, abs_grad_y;
const cv::String window_name = "Sobel Demo - Simple Edge Detector";
int ksize = 3;
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
cv::Sobel(img_gray, grad_x, ddepth, 1, 0, ksize, scale, delta, cv::BORDER_DEFAULT);
cv::Sobel(img_gray, grad_y, ddepth, 0, 1, ksize), scale, delta, cv::BORDER_DEFAULT;
cv::convertScaleAbs(grad_x, abs_grad_x);
cv::convertScaleAbs(grad_y, abs_grad_x);
cv::addWeighted(abs_grad_x, 0.5, abs_grad_x, 0.5, 0, grad);
cv::imshow(window_name, grad);
// cv::Mat kernel_x = (cv::Mat_(3, 3) << -1, 0, 1, -2, 0, 2, -2, 0, 2);
// cv::Mat kernel_y = (cv::Mat_(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
cv::Mat kernel_x = (cv::Mat_(3, 3) << -3, 0, 3, -10, 0, 10, -3, 0, 3);
cv::Mat kernel_y = (cv::Mat_(3, 3) << -3, -10, -3, 0, 0, 0, 3, 10, 3);
cv::Mat grad_k, grad_x_k, grad_y_k, abs_grad_x_k, abs_grad_y_k;
cv::filter2D(img_gray, grad_x_k, ddepth, kernel_x, cv::Point(-1, -1), 0, cv::BORDER_DEFAULT);
cv::filter2D(img_gray, grad_y_k, ddepth, kernel_y, cv::Point(-1, -1), 0, cv::BORDER_DEFAULT);
cv::convertScaleAbs(grad_x_k, abs_grad_x_k);
cv::convertScaleAbs(grad_y_k, abs_grad_y_k);
cv::addWeighted(abs_grad_y_k, 0.5, abs_grad_x_k, 0.5, 0, grad_k);
cv::imshow("Soble by Filtered2D", grad_k);
cv::imshow("Origin", img_gray);
cv::imwrite("sobel.png", grad);
cv::waitKey(0);
}
梯度图像:
3.Roberts算子【1965】
3.1基本理论
罗伯特交叉梯度算子是最早使用具有对角性能的二维核的算子之一,罗伯特算子的实现方式为对角差分,x方向
g x = ∂ f ∂ x = ( z 9 − z 5 ) g_x = \frac{\partial f}{\partial x}=(z_9 -z_5)g x =∂x ∂f =(z 9 −z 5 )算子为[ − 1 0 0 1 ] \begin{bmatrix} -1 &0 \ 0& 1 \end{bmatrix}[−1 0 0 1 ], y方向g y = ∂ f ∂ y = ( z 8 − z 6 ) g_y = \frac{\partial f}{\partial y}=(z_8 -z_6)g y =∂y ∂f =(z 8 −z 6 ),算子为[ 0 − 1 − 1 0 ] \begin{bmatrix} 0&-1 \ -1& 0 \end{bmatrix}[0 −1 −1 0 ], 2×2的核概念上简单,但不如中心对称的核准确。
3.2OpenCV API
OpenCV中没有专门的函数实现Roberts算子。可以借助 filter2D()
函数实现
void cv::filter2D (
InputArray src,
OutputArray dst,
int ddepth,
InputArray kernel,
Point anchor = Point(-1,-1),
double delta = 0,
int borderType = BORDER_DEFAULT
)
src
输入图像dst
与src
同shape
ddepth
目标图的深度,uint8
等kernel
单通道卷积核,浮点矩阵,若想在不同的通道上使用不同的卷积核则将原图像按channel
拆分,再使用不同的卷积核计算anchor
卷积的作用点,(-1, -1)表示卷积核的中心delta
卷积后追加的偏置borderType
边缘像素的处理方式
anchor
的使用方式
; 3.3示例
void robertsOperator(cv::Mat &img)
{
int ddepth = CV_16S;
cv::GaussianBlur(img, img, cv::Size(5, 5), 0, 0, cv::BORDER_DEFAULT);
cv::Mat img_gray;
cv::cvtColor(img, img_gray, cv::COLOR_BGR2GRAY);
cv::Mat kernel_x = (cv::Mat_(2, 2) << -1, 0, 0, 1);
cv::Mat kernel_y = (cv::Mat_(2, 2) << 0, -1, 1, 0);
cv::Mat grad_k, grad_x_k, grad_y_k, abs_grad_x_k, abs_grad_y_k;
cv::filter2D(img_gray, grad_x_k, ddepth, kernel_x, cv::Point(-1, -1), 0, cv::BORDER_DEFAULT);
cv::filter2D(img_gray, grad_y_k, ddepth, kernel_y, cv::Point(-1, -1), 0, cv::BORDER_DEFAULT);
cv::convertScaleAbs(grad_x_k, abs_grad_x_k);
cv::convertScaleAbs(grad_y_k, abs_grad_y_k);
cv::addWeighted(abs_grad_y_k, 0.5, abs_grad_x_k, 0.5, 0, grad_k);
cv::imshow("robert by Filtered2D", grad_k);
cv::imshow("Origin", img_gray);
cv::imwrite("roberts.png", grad_k);
cv::waitKey(0);
}
4.Prewitt算子
4.1基本理论
Prewitt
算子与 Sobel
算子十分类似,区别在于 Sobel
算子中心值为2,能够平滑噪声, Prewitt
算子实现起来更简单,形如, x方向
和y方向分别为:
[ − 1 0 1 − 2 0 2 − 1 0 1 ] \begin{bmatrix} -1 & 0 & 1\ -2& 0 & 2\ -1& 0 & 1 \end{bmatrix}⎣⎡−1 −2 −1 0 0 0 1 2 1 ⎦⎤
[ − 1 − 2 − 1 0 0 0 1 2 1 ] \begin{bmatrix} -1 & -2 & -1\ 0& 0 & 0\ 1& 2 & 1 \end{bmatrix}⎣⎡−1 0 1 −2 0 2 −1 0 1 ⎦⎤
4.2OpenCV API
使用 filter2D
实现
4.3示例
void prewittOperator(cv::Mat &img)
{
int ddepth = CV_16S;
cv::GaussianBlur(img, img, cv::Size(5, 5), 0, 0, cv::BORDER_DEFAULT);
cv::Mat img_gray;
cv::cvtColor(img, img_gray, cv::COLOR_BGR2GRAY);
cv::Mat kernel_x = (cv::Mat_(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
cv::Mat kernel_y = (cv::Mat_(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
cv::Mat grad_k, grad_x_k, grad_y_k, abs_grad_x_k, abs_grad_y_k;
cv::filter2D(img_gray, grad_x_k, ddepth, kernel_x, cv::Point(-1, -1), 0, cv::BORDER_DEFAULT);
cv::filter2D(img_gray, grad_y_k, ddepth, kernel_y, cv::Point(-1, -1), 0, cv::BORDER_DEFAULT);
cv::convertScaleAbs(grad_x_k, abs_grad_x_k);
cv::convertScaleAbs(grad_y_k, abs_grad_y_k);
cv::addWeighted(abs_grad_y_k, 0.5, abs_grad_x_k, 0.5, 0, grad_k);
cv::imshow("prewitt by Filtered2D", grad_k);
cv::imshow("Origin", img_gray);
cv::imwrite("prewitt.png", grad_k);
cv::waitKey(0);
}
5.其他
此外还有 Kirsch
罗盘核,使用8个不同核,在8个方向上计算梯度,进行边缘检测
Tips
- convertScaleAbs
Original: https://blog.csdn.net/lx_ros/article/details/122726085
Author: 恒友成
Title: (七)图像处理中常用算子LaplacianSobelRobertsPrewittKirsch
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/641098/
转载文章受原作者版权保护。转载请注明原作者出处!