效果
- 左上为y方向滤波,右上为x方向滤波,下图为两种滤波的叠加
原理部分
线性滤波可以说是图像处理最基本的方法,它允许我们对图像进行处理,产生很多不同的效果。
卷积
卷积的数学定义:
一般称g为作用在f上的filter或kernel
对于滤波器,也有一定的规则要求:
- 滤波器的大小应该是奇数,这样它才有一个中心,例如3x3,5x5或者7x7。有中心了,也有了半径的称呼,例如5x5大小的核的半径就是2。
- 滤波器矩阵所有的元素之和应该要等于1,这是为了保证滤波前后图像的亮度保持不变。但这不是硬性要求。
- 如果滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像更亮,反之,如果小于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗。
- 对于滤波后的结构,可能会出现负数或者大于255的数值。对这种情况,我们将他们直接截断到0和255之间即可。对于负数,也可以取绝对值。只针对图像。
在具体应用中,往往有多个卷积核,可以认为,每个卷积核代表了一种图像模式。如果某个图像块与此卷积核卷积出的值大,则认为此图像块十分接近于卷积核。
-
例如,如果我们设计了6个卷积核,可以理解:我们认为这个图像上有6种底层纹理模式,也就是我们永6种基础模式就能描绘出一幅图像。
-
用Gx或Gy来卷积下面这张图的话,就会在中间黑白边界获得比较大的值。
在非边界部分,得到结果为0(黑色),在边界部分,得到结果取绝对值并截断为255(白色)
卷积的应用——一个没有任何效果的卷积
- 将原像素中间像素值乘1,其余全部乘0。
显然像素值不会发生任何变化。
平滑均值滤波
- 取九个值的平均值代替中间像素值,起到平滑的效果。
高斯平滑
- 高斯平滑水平和垂直方向呈现高斯分布,更突出了中心点在像素平滑后的权重,相比于均值滤波而言,有着更好的平滑效果。
图像锐化
- 图像锐化使用的是拉普拉斯变换核函数
Sobel边缘检测
- Sobel更强调了和边缘相邻的像素点对边缘的影响。
卷积解决的问题
- 卷积负责提取图像中的局部特征
卷积–步长/Stride
如果用(f, f)的过滤器来卷积一张(h, w)大小的图片,每次移动一个像素的话,那么得出的结果就是(h-f+1, w-f+1)的输出结果。f是过滤器大小,h和w分别是图片的高宽。
如果每次不止移动一个像素,而是s个像素,那么结果就会变为:
这个s就叫做步长
存在的问题:
- 只要是f或s的值比1要大的话,那么每次卷积之后结果的长款,要比卷积之前小一些
- 丢失信息
因此,我们可以在外围补上一圈值为0的像素
有了填充之后,每次卷积之后的图像大小:
p表示填充的padding圈数,每填充一圈长/宽增加2
此时如果想让高(宽)不变(步长为1时):
计算得到:
卷积——三种填充模式
橙色部分为image, 蓝色部分为filter。从左往右分别是:full、same、valid模式
注意:这里的same还有一个意思,卷积之后输出的feature map尺寸保持不变(相对于输入图片)。当然,same模式不代表完全输入输出尺寸一样,也跟卷积核的步长有关系。same模式也是最常见的模式,因为这种模式可以在卷积过程中让图的大小保持不变。
多通道卷积
- 蓝色部分表示原图的三个通道映射图片
- 红色的两列W0, W1表示对应着两种特征提取。因为这里图像分解为三通道,所以每个特征提取还要再细分为对三个通道的不同卷积
- 对于一个特征提取(如W0),计算方式为,将三个通道的卷积分别计算后累加,得到Output。绿色部分的两幅图则分别对应W0列,W1列的三个通道卷积累加的计算结果
- 注意:上述例子的卷积计算中的步长stride = 2
卷积核的确定
CNN厉害的地方在于,过滤器的特征并不是人为设定的,而是通过大量图片自己训练出来的
代码部分(以Sobel边缘检测为例)
"""
@author: Hanley-Yang
sobel:边缘检测
"""
import cv2
from matplotlib import pyplot as plt
'''
Sobel函数求完导数后会有负值,还有会大于255的值。
而原图像是uint8,即8位无符号数(范围在[0,255]),所以Sobel建立的图像位数不够,会有截断。
因此要使用16位有符号的数据类型,即cv2.CV_16S
'''
img = cv2.imread('Euphonium.png')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
x = cv2.Sobel(img_gray, cv2.CV_16S, 1, 0)
y = cv2.Sobel(img_gray, cv2.CV_16S, 0, 1)
'''
在经过处理后,别忘了用convertScaleAbs()函数将其转回原来的uint8形式。
否则将无法显示图像,而只是一副灰色的窗口。
dst = cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])
其中可选参数alpha是伸缩系数,beta是加到结果上的一个值。结果返回uint8类型的图片。
'''
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
'''
由于Sobel算子是在两个方向计算的,最后还需要用cv2.addWeighted(...)函数将其组合起来
。其函数原型为:
dst = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])
其中alpha是第一幅图片中元素的权重,beta是第二个的权重,
gamma是加到最后结果上的一个值。
'''
dst = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
rgb_absX = cv2.cvtColor(absX, cv2.COLOR_BGR2RGB)
rgb_absY = cv2.cvtColor(absY, cv2.COLOR_BGR2RGB)
rgb_dst = cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)
plt.subplot(221)
plt.imshow(rgb_absX)
plt.subplot(222)
plt.imshow(rgb_absY)
plt.subplot(223)
plt.imshow(rgb_dst)
plt.show()
- 输出结果
左上为y方向滤波,右上为x方向滤波,下图为两种滤波的叠加