卷积(convolution)和图像滤镜(filter)

2020年12月7日 / 465次阅读 / Last Modified 2021年1月12日
图像处理

本文记录本人对卷积(convolution)和图像滤镜(filter)这两个概念的认识和理解,配合一些测试代码。

第一次听到卷积这个词,还是通过几年前的卷积神经网络(CNN),当时一头雾水。后来不断学习,接触了图像处理,也接触到各种图像滤镜的算法。恍然大悟,卷积就是图像滤镜实现的基础。

卷积其实就是一个这样的动作:将图像中每一个pixel的值进行修改,修改方式为此pixel以及它周围的pixel的weighted sum。每个pixel周围一圈的pixel都要参与计算,每个位置的权重事先确定(filter),或者通过学习(CNN)。由于要计算每个pixel周围所有的pixel,中文翻译中就出现了卷这个字。计算最后再做一个normalization。

卷积(convolution)操作
卷积(convolution)操作

上图中间那个小正方形,叫做卷积核(convolution kernel),它存放的是每个pixel的权重。注意,中间那个pixel也要参与计算。上图最后得到的数字是-8,如果这是对图像进行卷积,-8这个值还要正规化(normalization)。

Bloody Details:

  • 卷积核的长和宽不需要一定相同,即kernel可以是长方形的,但常见的好像都是正方形的,比如3*3,5*5等;
  • 卷积核的长和宽,一定要是奇数;
  • 卷积核(滤波器矩阵)所有的元素之和应该要等于1,这是为了保证滤波前后图像的亮度保持不变。当然了,这不是硬性要求了;
  • 如果卷积核所有元素之和大于1,那么滤波后的图像就会比原图像更亮,反之,如果小于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗;(?)
  • 对于计算结果的normalization,对于滤波后的结果,可能会出现负数或者大于255的数值。对这种情况,我们将他们直接截断到0和255之间即可。(对于浮点数,小于0的都是0,大于1的都是1,?)
  • boundary的处理,有各种方法。比如是3*3的kernel,最外面一圈可以选择不处理,或者直接copy neighbor,或者再外面附一圈值(增加一圈pixel),让convolution计算可以覆盖了最边缘。
  • 不同颜色channel的图像(RGB,3个channel),还可以使用不同的核。

如果我没记错,卷积神经网络(CNN),kernel中的权重,是自己学习得到的!这部分暂时不深入了,下面开始记录filter操作。

图片在经过kernel convolution操作之后,每一个pixel的值都发生了变化(除了最边上的那一圈),这种变化的依据来自pixel周围,以及确定的权重。这就是图像的滤镜操作!(这种操作,有的地方叫做线性滤波,卷积核叫做滤波矩阵。)

这里介绍的卷积操作,是线性的,即每个pixel的值都是周围值的线性组合。而且具有平移不变形(shift-invariant),即对每一个不同位置的pixel,使用相同的kernel。这些都是很学术的概念。还有个说法,2D卷积

下面开始研究不同的kernel,以得到不同的滤镜效果!我们使用scipy.ndimage.convolve接口,用matplotlib显示图像,当然还有必不可少的numpy模块。(matplotlib的imshow接口,可以自动对数据进行normalization操作)

>>> from scipy.ndimage import convolve
>>> import matplotlib.pyplot as plt
>>> import matplotlib.image as mpimg
>>> import numpy as np

先看看原始图片的样子:

>>> img = mpimg.imread('dog.png')
>>> img.shape
(400, 400, 4)
>>> dog = img[:,:,:3]
>>> plt.ion()
>>> plt.imshow(dog)
>>> plt.axis('off')

边缘检测,Edge Detect

先试试这个检测所有边缘的卷积核:

>>> edge_detect = np.array(((-1,-1,-1),(-1,8,-1),(-1,-1,-1)))
>>> edge_detect
array([[-1, -1, -1],
       [-1,  8, -1],
       [-1, -1, -1]])

边缘是高频信号,即画面有突变的地方(像素点),上面这个核作用于图像后,中心点与周围一圈相同或接近相同的区域(3*3大小),中心点的值会变成0(或无线接近),而中心点与周围不相同的区域,会得到一个远离0的值。

下面看一下得到的图像:

>>> e = edge_detect
>>> dog1 = convolve(dog, np.dstack((e,e,e)))
>>> plt.imshow(dog1)
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
<matplotlib.image.AxesImage object at 0x7f729dd0b700>

Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). 这就是matplotlib默认进行的normalization操作,但具体细节不详。

由于是彩色图像,我们用np.dstack制作一个3维的核用于convolve计算,即图像每个颜色channel用一样的卷积核处理。效果如下:

边缘检测的卷积核很多,下面试试合格水平边缘检测的卷积核:

>>> e2
array([[ 0,  0,  0],
       [-1,  1,  0],
       [ 0,  0,  0]])
>>> dog2 = convolve(dog, np.dstack((e2,e2,e2)))
>>> plt.imshow(dog2)

这个核计算每个像素点与它左边的点的值的差!有人说这就是斜率。效果如下:

我感觉这个图有部分边缘不够清晰,于是用灰度图重新试了一下,代码和效果如下:

>>> e2
array([[ 0,  0,  0],
       [-1,  1,  0],
       [ 0,  0,  0]])
>>> dog3 = convolve(dog[:,:,0], e2)
>>> plt.imshow(dog3, cmap='gray')

这次边缘卡着舒服了一点。有没有注意到边缘的一圈黑线,估计这就是convolve函数对图像boundary的处理特点。

还有5*5的水平边缘检测卷积核:

>>> e3 = np.array(((0,0,0,0,0),(0,0,0,0,0),(-1,-1,2,0,0),(0,0,0,0,0),(0,0,0,0,0)))
>>> e3
array([[ 0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0],
       [-1, -1,  2,  0,  0],
       [ 0,  0,  0,  0,  0],
       [ 0,  0,  0,  0,  0]])

还有垂直方向,45度叫方向等等各种花样。

浮雕,emboss

>>> f1 = np.array(((-2,-2,0),(-2,6,0),(0,0,0)))
>>> f1
array([[-2, -2,  0],
       [-2,  6,  0],
       [ 0,  0,  0]])
>>> dog5 = convolve(dog, np.dstack((f1,f1,f1)))
>>> plt.imshow(dog5)

以上代码生成的图像效果:

有那么一点点立体的效果。同样,用于emboss效果的核也是各种各样。

模糊,blur

>>> b = np.array(((1,1,1),(1,1,1),(1,1,1)))
>>> b
array([[1, 1, 1],
       [1, 1, 1],
       [1, 1, 1]])

上例的这个全1的kernel,有人说是 box blur。

每个pixel的值,都有周围的值一起取个平均,画面就模糊了。

锐化,sharp

用一个最简单的3*3的锐化核试试效果:

>>> r = np.array(((-1,-1,-1),(-1,9,-1),(-1,-1,-1)))
>>> r
array([[-1, -1, -1],
       [-1,  9, -1],
       [-1, -1, -1]])
>>> dog6 = convolve(dog, np.dstack((r,r,r)))
>>> plt.imshow(dog6/4)

在imshow的时候,我用了dog6/4,如果不这样,得到的图像太亮了!就算这样做了,这个图我感觉也有些不对劲,草地变成了枯黄的颜色。应该是normalization过程除了什么问题,上面代码是使用matplitlib默认的normalization,估计这个默认的方法是有点问题的。anyway,看看这只狗狗的脸和胸口,纹理更加清晰可见,这就是锐化的效果。

测试到这里暂停吧,至少说清楚了图像滤镜和卷积的基本原理。normalization的问题,以后再研究。

-- EOF --

本文链接:https://www.pynote.net/archives/2945

留言区

《卷积(convolution)和图像滤镜(filter)》有3条留言

您的电子邮箱地址不会被公开。 必填项已用*标注

  • 麦新杰

    edge detction(边缘检测)对图像识别中的特征提取是非常有作用的,边缘检测卷积核都有一个共同点,就是能够突出图片矩阵中变化剧烈的位置。 [回复]

  • 麦新杰

    本文代码有个normalization的问题,问题出在图像数据范围不再0-255,而是float;但我测试时,所有的kernel,都是int形式的数据。 [回复]

  • 麦新杰

    卷积核(kernel),也叫卷积矩阵(convolution matrix)或者掩膜(mask),本质上是一个非常小的矩阵,最常用的是 3×3 矩阵。主要是利用核与图像之间进行卷积运算来实现图像处理,能做出模糊、锐化、凹凸、边缘检测等效果。 [回复]


前一篇:
后一篇:

More


©Copyright 麦新杰 Since 2019 Python笔记

go to top