2020年12月7日 / 895次阅读 / Last Modified 2021年1月12日
图像处理
本文记录本人对卷积(convolution)和图像滤镜(filter)这两个概念的认识和理解,配合一些测试代码。
第一次听到卷积这个词,还是通过几年前的卷积神经网络(CNN),当时一头雾水。后来不断学习,接触了图像处理,也接触到各种图像滤镜的算法。恍然大悟,卷积就是图像滤镜实现的基础。
卷积其实就是一个这样的动作:将图像中每一个pixel的值进行修改,修改方式为此pixel以及它周围的pixel的weighted sum。每个pixel周围一圈的pixel都要参与计算,每个位置的权重事先确定(filter),或者通过学习(CNN)。由于要计算每个pixel周围所有的pixel,中文翻译中就出现了卷这个字。计算最后再做一个normalization。
上图中间那个小正方形,叫做卷积核(convolution kernel),它存放的是每个pixel的权重。注意,中间那个pixel也要参与计算。上图最后得到的数字是-8,如果这是对图像进行卷积,-8这个值还要正规化(normalization)。
Bloody Details:
如果我没记错,卷积神经网络(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 = 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度叫方向等等各种花样。
>>> 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效果的核也是各种各样。
>>> 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的值,都有周围的值一起取个平均,画面就模糊了。
用一个最简单的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条留言
前一篇:struct模块的使用
后一篇:哥伦布(Golomb)压缩编码
©Copyright 麦新杰 Since 2019 Python笔记
edge detction(边缘检测)对图像识别中的特征提取是非常有作用的,边缘检测卷积核都有一个共同点,就是能够突出图片矩阵中变化剧烈的位置。 [ ]
本文代码有个normalization的问题,问题出在图像数据范围不再0-255,而是float;但我测试时,所有的kernel,都是int形式的数据。 [ ]
卷积核(kernel),也叫卷积矩阵(convolution matrix)或者掩膜(mask),本质上是一个非常小的矩阵,最常用的是 3×3 矩阵。主要是利用核与图像之间进行卷积运算来实现图像处理,能做出模糊、锐化、凹凸、边缘检测等效果。 [ ]