专注于做有价值的技术原创

0%

卷积神经网络可视化——Image Kernel

前言

深度学习领域,解决图像分类问题,最常用的就是卷积神经网络(Convolutional Neural Network)简称 CNN。之所以称之卷积神经网络,是因为,隐藏层中使用了卷积层,来处理二维(灰度)或三维(RGB)的图像数据。每个卷积层由多个过滤器(Filter)组成,每个过滤器对应一个小矩阵(行列数通常为2或者3),矩阵沿着图像的行列,按一定步长,依次乘积,求和,得到一副新的图像数据,这个过程就是对图像求卷积的过程。

在卷积神经网络的应用中,对图像求卷积得到一副新的图像(如果步长大于1,新图像会缩小),通常用于提取图像的特征,每个过滤器也被称为一个特征映射。但是,图像卷积不只是用在深度神经网络提取特征,在深度学习应用之前,图像卷积已被大量应用在图像处理领域。如我们熟悉的 PhotoShop 等图像处理软件中的,模糊,锐化,浮雕,轮廓线,以及边沿检测,也是通过图像卷积实现的。不同的是,深度学习的卷积矩阵数值是由模型训练出来的,没有规律,而图像处理中,矩阵的数值是有规律的,并且形状通常为 3行3列。我们也称这个矩阵为 Image Kernel (事实上,在 Keras 中卷积层参数也使用 kernel 表示过滤器尺寸)。

本文标题叫《卷积神经网络可视化》,事实上,也是为了说明,虽然神经网络的参数就像黑盒子,无法解释,但是卷积层的输出是可以可视化呈现的。本文从图像处理角度,来解释用于图像卷积的不同 Image Kernel 的可视化输出结果。

1. 导入库

1
2
3
4
from PIL import Image, ImageDraw
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

2. 实现 Image Kernel 运算

2.1 助手函数

指定图像路径,使用 PIL.Image 读取彩色图像或者灰度图像。

1
2
3
4
5
def read_img(imgpath, gray=True):
img = Image.open(imgpath)
if gray:
img = img.convert("L")
return img

指定图像宽度,按比例缩放图像。

1
2
3
4
def resize_width(img, width):
w, h = img.size
height = int(width * h / w)
return img.resize((width, height))

以图像最短边长,从中间截取正方形图像。

1
2
3
4
5
6
def crop_center(img):
w, h = img.size
c_x, c_y = w/2, h/2
offset = min(w, h) / 2
crop_box = (c_x-offset, c_y-offset, c_x+offset, c_y+offset)
return img.crop(crop_box)

2.2 指定 kernel 对图像处理

img 为 Image 对象;kernel 为进行卷积运算的矩阵;strides 为卷积运算单次平移步长;mean 为 False 时,求和,为 True 时,求平均值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def apply_img_kernel(img, kernel, strides=1, mean=False):
img = np.asarray(img)

h, w = img.shape[:2]
k_h, k_w = kernel.shape[:2]
x_range = range(0, w - k_w + 1, strides)
y_range = range(0, h - k_h + 1, strides)

if mean:
prosum = lambda a,b: min((a*b).mean(), 255)
else:
prosum = lambda a,b: min((a*b).sum(), 255)

cal = lambda img: np.array([[prosum(img[i:i+k_h, j:j+k_w], kernel)
for j in x_range]
for i in y_range]).astype(np.uint8)

if len(img.shape) == 2:
data = cal(img)
return Image.fromarray(data)
elif len(img.shape) == 3:
r, g, b = np.transpose(img, (2, 0, 1))
_r, _g, _b = cal(r), cal(g), cal(b)
return Image.merge('RGB', [Image.fromarray(d) for d in [_r, _g, _b]])

3. 不同效果的 Image Kernel

亮化,锐化,模糊,浮雕,轮廓线,边界线。每个 kernel 都是函数,可以调节参数,默认参数取自网站 http://setosa.io/ev/image-kernels

3.1 亮化

1
2
3
4
def identity_kernel(iden=1.0):
return np.array([[0, 0, 0],
[0, iden, 0],
[0, 0, 0]])

3.2 锐化

1
2
3
4
def sharpen_kernel(inner=5.0,  edge=-1.0):
return np.array([[0, edge, 0],
[edge, inner, edge],
[0, edge, 0]])

3.3 模糊

1
2
3
4
def blur_kernel(inner=0.25,  edge=0.125, corner=0.0625):
return np.array([[corner, edge, corner],
[edge, inner, edge],
[corner, edge, corner]])

3.4 浮雕

1
2
3
4
def emboss_kernel(diag=2.0, iden=1.0):
return np.array([[-diag, -iden, 0],
[-iden, iden, iden],
[0, iden, diag]])

3.5 轮廓线

1
2
3
4
def outline_kernel(inner=8.0, outer=-1.0):
return np.array([[outer, outer, outer],
[outer, inner, outer],
[outer, outer, outer]])

3.6 边缘检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def sobel_kernel(direction, base=None, edge=2.0, corner=1.0):
if base is not None:
edge = base
corner = base / 2
if direction == 'top':
return np.array([[corner, edge, corner], [0, 0, 0], [-corner, -edge, -corner]])
elif direction == 'bottom':
return np.array([[-corner, -edge, -corner], [0, 0, 0], [corner, edge, corner]])
elif direction == 'left':
return np.array([[corner, 0, -corner], [edge, 0, -edge], [corner, 0, -corner]])
elif direction == 'right':
return np.array([[-corner, 0, corner], [-edge, 0, edge], [-corner, 0, corner]])

return identity_kernel()

4. 使用效果

读取图片,并进行缩放裁剪处理。(手机拍摄图片太大,运算时间长,图像显示也很占空间)

tips: gray 参数设置 True, 读取灰度图像。这里演示彩色图像效果。

1
2
3
img = read_img('yuki.jpeg', gray=False)

img = crop_center(resize_width(img, 320))

助手函数:在一行显示两幅图片,用于对比实施 Kernel 前后的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def show2imgs(img1, img2, title=None):
fig = plt.figure(figsize=(10, 5))

if title is not None:
fig.suptitle(title)

plt.subplot(121)
plt.axis('off')
plt.imshow(img1)

plt.subplot(122)
plt.axis('off')
plt.imshow(img2)

plt.show()

4.1 亮化

1
2
3
img_identity = apply_img_kernel(img, identity_kernel(1.6))

show2imgs(img, img_identity)

4.2 锐化

1
2
3
img_sharpen = apply_img_kernel(img, sharpen_kernel(inner=1.7,  edge=-0.08))

show2imgs(img, img_sharpen)

4.3 模糊

1
2
3
img_blur = apply_img_kernel(img, blur_kernel())

show2imgs(img, img_blur)

4.4 浮雕

1
2
3
img_emboss = apply_img_kernel(img, emboss_kernel())

show2imgs(img, img_emboss)

4.5 轮廓线

1
2
3
img_outline = apply_img_kernel(img, outline_kernel(inner=8.9, outer=-1.29), mean=True)

show2imgs(img, img_outline)

4.6 边缘检测

1
2
3
4
5
6
7
img_sobel_top = apply_img_kernel(img, sobel_kernel('top', 0.03))
img_sobel_bottom = apply_img_kernel(img, sobel_kernel('bottom', 0.03))
img_sobel_left = apply_img_kernel(img, sobel_kernel('left', 0.03))
img_sobel_right = apply_img_kernel(img, sobel_kernel('right', 0.03))

show2imgs(img_sobel_top, img_sobel_bottom)
show2imgs(img_sobel_left, img_sobel_right)

5. 开源

本文代码开源,可通过github下载:https://github.com/kenblikylee/imgkernel

也可使用 pip 直接安装体验(使用方式见github主页):

1
pip install imgkernel
青笔 wechat
我是一条小青蛇 我有很多小秘密