文章目录
S1.2 如何自定义核?
我们知道很多的卷积核,例如sobel,均值滤波核,高斯核,拉普拉斯变换核。这些核一般是3x3的矩阵。也有5x5的。
均
值
1
9
[
1
,
1
,
1
1
,
1
,
1
1
,
1
,
1
]
均值 \frac{1}{9}\begin{bmatrix} 1,1,1\\ 1,1,1\\ 1,1,1\\ \end{bmatrix}
均值91⎣⎡1,1,11,1,11,1,1⎦⎤
S o b e l [ − 1 , 0 , 1 − 2 , 0 , 2 − 1 , 0 , 1 ] S c h a r r [ − 3 , 0 , 3 − 10 , 0 , 10 − 3 , 0 , 3 ] Sobel \begin{bmatrix} -1,&0,&1\\ -2,&0,&2\\ -1,&0,&1\\ \end{bmatrix} Scharr \begin{bmatrix} -3,&0,&3\\ -10,&0,&10\\ -3,&0,&3\\ \end{bmatrix} Sobel⎣⎡−1,−2,−1,0,0,0,121⎦⎤Scharr⎣⎡−3,−10,−3,0,0,0,3103⎦⎤
5 x 5 高 斯 [ 1 , 4 , 7 , 4 , 1 4 , 16 , 26 , 16 , 4 7 , 26 , 41 , 26 , 7 4 , 16 , 26 , 16 , 4 1 , 4 , 7 , 4 , 1 ] 5x5高斯 \begin{bmatrix} 1,&4,&7,&4,&1\\ 4,&16,&26,&16,&4\\ 7,&26,&41,&26,&7\\ 4,&16,&26,&16,&4\\ 1,&4,&7,&4,&1\\ \end{bmatrix} 5x5高斯⎣⎢⎢⎢⎢⎡1,4,7,4,1,4,16,26,16,4,7,26,41,26,7,4,16,26,16,4,14741⎦⎥⎥⎥⎥⎤
拉 普 拉 斯 [ 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,010⎦⎤
(ps:什么是卷积?S0.7 卷积和相关)
那么问题来了:能不能自己定义卷积核呢?这些卷积核都是怎么设计的呢?如何理解这些卷积核呢?
这篇blog博主小白就斗胆探究一下怎样设计卷积核-。-
构建自定义核程式
我们用OpenCV自定义核函数filter2D,来作为我们的源码。当然你可以把部分功能封装成一个函数。
//25. 自定义卷积核
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat src = imread("images/favorite/Lena.jpg",0);
Mat dst;
imshow("src", src);
double matrix[3][3] = { {1, 1, 1},
{1, 1, 1},
{1, 1, 1}};
Mat filter0(Size(3,3), CV_64F, matrix);
filter0 = filter0/9;
// std::cout << filter0 << std::endl;
filter2D(src, dst, CV_64F, filter0);
dst.convertTo(dst, CV_8U);
imshow("dst", dst);
waitKey(0);
return 0;
}
分析不同卷积核效果
均值核,高斯核
这两个核里面都是正数,无非就是像素值加权平均,最终只会把像素变的越来越相同,产生的图像会模糊,但会有滤波效果。总结起来就是,如果相邻是正数,表示权值大小关系
中心为锚点(实际值所在的地方),中心越大,图像越不模糊,滤波效果减弱。
拉普拉斯变换
拉普拉斯常用于边缘提取,它的特点是中心是负数,而且核的sum=0。
由于负数在卷积核中对应梯度操作(之后会讲),我们得到的图像其实是那些图像对x和y梯度的叠加。知道这个,我们可以把拉普拉斯变换变成两个:一个是对x的梯度,一个是对y的梯度:
对
x
[
0
,
0
,
0
1
,
−
2
,
1
0
,
0
,
0
]
对
y
[
0
,
1
,
0
0
,
−
2
,
0
0
,
1
,
0
]
对x \begin{bmatrix} 0,&0,&0\\ 1,&-2,&1\\ 0,&0,&0\\ \end{bmatrix} 对y \begin{bmatrix} 0,&1,&0\\ 0,&-2,&0\\ 0,&1,&0\\ \end{bmatrix}
对x⎣⎡0,1,0,0,−2,0,010⎦⎤对y⎣⎡0,0,0,1,−2,1,000⎦⎤
如果我们想得到既对x,也对y的就把两个核产生的图像加起来。
图中src原图,dst0拉普拉斯变换图,dst1、dst2对x梯度对y的梯度,dst3为dst1+dst2。
- 只要有负数就可以提取边缘?
是的,我们演示三个自己写的核。
d
s
t
0
[
1
,
0
,
1
0
,
0
,
0
0
,
−
2
,
0
]
dst0 \begin{bmatrix} 1,&0,&1\\ 0,&0,&0\\ 0,&-2,&0\\ \end{bmatrix}
dst0⎣⎡1,0,0,0,0,−2,100⎦⎤
d s t 1 [ 0 , − 1 , 0 1 , 1 , 1 0 , − 2 , 0 ] dst1\begin{bmatrix} 0,&-1,&0\\ 1,&1,&1\\ 0,&-2,&0\\ \end{bmatrix} dst1⎣⎡0,1,0,−1,1,−2,010⎦⎤
d s t 2 [ 0 , 0 , 3 2 , 0 , 0 0 , − 5 , 0 ] dst2\begin{bmatrix} 0,&0,&3\\ 2,&0,&0\\ 0,&-5,&0\\ \end{bmatrix} dst2⎣⎡0,2,0,0,0,−5,300⎦⎤
第一个提取的边缘比较厚,第二个比较薄。这是因为第一个由于有一行0,导致边缘附近的也可以检测到边缘,导致边缘变大,这样有利于放大 小的边缘。
- 只是中心变成负数?
[ 1 , 1 , 1 1 , − 8 , 1 1 , 1 , 1 ] \begin{bmatrix} 1,&1,&1\\ 1,&-8,&1\\ 1,&1,&1\\ \end{bmatrix} ⎣⎡1,1,1,1,−8,1,111⎦⎤
我们得到了一个提取边缘的卷积核,其实这个核和拉普拉斯没啥区别,感觉就像考虑4邻域和8邻域的图像边缘提取。
- 换一下?边缘变成负数?
[ 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−10⎦⎤
其实没多大区别。
Sobel核
在很多教材中会提到Sobel其实可以表示为两个一维矩阵的卷积:
S
o
b
e
l
[
−
1
,
0
,
1
−
2
,
0
,
2
−
1
,
0
,
1
]
Sobel \begin{bmatrix} -1,&0,&1\\ -2,&0,&2\\ -1,&0,&1\\ \end{bmatrix}
Sobel⎣⎡−1,−2,−1,0,0,0,121⎦⎤
其实根据之前讨论的零行就是扩大边缘,sobel其实理解起来并没有什么问题。
不
就
是
[
0
,
0
,
0
−
2
,
0
,
2
0
,
0
,
0
]
的
增
强
版
么
?
不就是 \begin{bmatrix} 0,&0,&0\\ -2,&0,&2\\ 0,&0,&0\\ \end{bmatrix} 的增强版么?
不就是⎣⎡0,−2,0,0,0,0,020⎦⎤的增强版么?
自己动手DIY自己的卷积核
。。。
卷积核的数学分析
首先,我们定义卷积函数f在x方向和y方向的的二阶导:
∂
2
f
∂
x
2
=
f
(
x
+
1
,
y
)
+
f
(
x
−
1
,
y
)
−
2
f
(
x
,
y
)
∂
2
f
∂
y
2
=
f
(
x
,
y
+
1
)
+
f
(
x
,
y
−
1
)
−
2
f
(
x
,
y
)
\frac{\partial^2 f}{\partial x^2} = f(x+1,y)+f(x-1,y)-2f(x,y)\\ \frac{\partial^2 f}{\partial y^2} = f(x,y+1)+f(x,y-1)-2f(x,y)
∂x2∂2f=f(x+1,y)+f(x−1,y)−2f(x,y)∂y2∂2f=f(x,y+1)+f(x,y−1)−2f(x,y)
我们的拉普拉斯算子就是两者相加:
∇
2
f
(
x
,
y
)
=
f
(
x
+
1
,
y
)
+
f
(
x
−
1
,
y
)
+
f
(
x
,
y
+
1
)
+
f
(
x
,
y
−
1
)
−
4
f
(
x
,
y
)
\nabla^2f(x,y)=f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1)-4f(x,y)
∇2f(x,y)=f(x+1,y)+f(x−1,y)+f(x,y+1)+f(x,y−1)−4f(x,y)
卷积核的频域体现(傅里叶变换卷积定理)
我们来做一道题:
找出一个等价的滤波器H(u,v),它在频率域实现拉普拉斯变换等价操作。
在空域中,滤波后的图像用函数表示:
g
(
x
,
y
)
=
f
(
x
+
1
,
y
)
+
f
(
x
−
1
,
y
)
+
f
(
x
,
y
+
1
)
+
f
(
x
,
y
−
1
)
−
4
f
(
x
,
y
)
g(x,y)=f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1)-4f(x,y)
g(x,y)=f(x+1,y)+f(x−1,y)+f(x,y+1)+f(x,y−1)−4f(x,y)
我们可以很简单的证明:
f
(
x
+
y
0
,
x
+
y
0
)
经
过
傅
里
叶
变
换
变
成
F
(
u
,
v
)
e
−
2
π
i
(
u
x
0
M
+
v
y
0
N
)
f(x+y_0,x+y_0)经过傅里叶变换变成 F(u,v) e^{-2\pi i(\frac{ux_0}{M}+\frac{vy_0}{N})}
f(x+y0,x+y0)经过傅里叶变换变成F(u,v)e−2πi(Mux0+Nvy0)
我们假设频域变换前后图像有如下关系:
G
(
u
,
v
)
=
H
(
u
,
v
)
F
(
u
,
v
)
G(u,v)=H(u,v)F(u,v)
G(u,v)=H(u,v)F(u,v)
我们把第一个式子两边都傅里叶变换一下,解频域中的变换关系H:
F
(
u
,
v
)
[
e
−
2
π
i
u
M
+
e
2
π
i
u
M
+
e
−
2
π
i
v
N
+
e
2
π
i
v
N
]
=
H
(
u
,
v
)
F
(
u
,
v
)
H
(
u
,
v
)
=
e
−
2
π
i
u
M
+
e
2
π
i
u
M
+
e
−
2
π
i
v
N
+
e
2
π
i
v
N
F(u,v)[e^{-2\pi i\frac{u}{M}}+e^{2\pi i\frac{u}{M}}+e^{-2\pi i\frac{v}{N}}+e^{2\pi i\frac{v}{N}}]=H(u,v)F(u,v)\\ H(u,v)=e^{-2\pi i\frac{u}{M}}+e^{2\pi i\frac{u}{M}}+e^{-2\pi i\frac{v}{N}}+e^{2\pi i\frac{v}{N}}
F(u,v)[e−2πiMu+e2πiMu+e−2πiNv+e2πiNv]=H(u,v)F(u,v)H(u,v)=e−2πiMu+e2πiMu+e−2πiNv+e2πiNv
根据欧拉定理:
H
(
u
,
v
)
=
2
c
o
s
(
2
π
u
M
)
+
2
c
o
s
(
2
π
v
N
)
H(u,v)=2cos(\frac{2\pi u}{M})+2cos(\frac{2\pi v}{N})
H(u,v)=2cos(M2πu)+2cos(N2πv)
其他无关的东西
#include <iostream>
#include <string.h>
#include <opencv2/opencv.hpp>
using namespace cv;
//输入卷积核,显示结果
Mat filter_show(Mat src, double matrix[3][3], int m, int n, String name)
{
Mat dst;
Mat filter(Size(n,m), CV_64F, matrix);
double sum = 0;
for(int i = 0; i < n; i++)
for(int j = 0; j < m; j++)
sum += matrix[i][j];
if(sum != 0)
filter = filter/sum;
filter2D(src, dst, CV_64F, filter);
dst.convertTo(dst, CV_8U);
return dst;
// imshow(name, dst);
}
int main()
{
Mat src = imread("images/favorite/Lena.jpg",0);
imshow("src", src);
int m = 3;
int n = 3;
double matrix0[3][3] = { {0, 0, 0},
{-2, 0, 2},
{0, 0, 0}};
double matrix1[3][3] = { {0, 1, 0},
{1, -4, 1},
{0, 1, 0}};
double matrix2[3][3] = { {1, 1, 1},
{1, -8, 1},
{1, 1, 1}};
// std::cout << filter0 << std::endl;
Mat dst0 = filter_show(src, matrix0, n, m, "dst0");
Mat dst1 = filter_show(src, matrix1, n, m, "dst1");
Mat dst2 = filter_show(src, matrix2, n, m, "dst2");
imshow("dst0", dst0);
imshow("dst1", dst1);
imshow("dst2", dst2);
// imshow("dst3", dst1+dst2);
waitKey(0);
return 0;
}