文章目录
一、相关概念
1.卷积
对图像(不同的数据窗口数据)和滤波矩阵(一组固定的权重:因为每个神经元的多个权重固定,所以又可以看做一个恒定的滤波器filter)做内积(逐个元素相乘再求和)的操作就是所谓的『卷积』操作,也是卷积神经网络的名字来源。
2.卷积核
卷积核就是滤波矩阵,是做内积运算的对象,决定了滑动窗口的大小和范围。
3.多通道
输入图像的通道数根据图片特性和自己需求自己确定,输入图像的通道决定了卷积核的通道,卷积核的个数又决定了特征图的通道数。
4.特征图
图像与卷积核加权累加得到卷积(滤波)后的特征图。
5.特征选择
人的视觉神经细胞对不同的视觉模式具有特征选择性, 不同视觉神经细胞对边界、运动和颜色等不同信息具有强弱不同的选择性。 不同卷积核 可被用来刻画 不同选择性。
二、探究多种卷积核的作用和原理
多种卷积核的作用
这里的测试都是来自测试网站
1、边缘检测
边缘检测的目的就是找到图像中亮度变化剧烈的像素点构成的集合,表现出来往往是轮廓。
2、锐化
锐化可以加强图像中的边界和细节信息。
3、 模糊
达到视觉上模糊的效果。
原理的分析
网上查阅了一下,讲解都看不太懂(可能个人能力有限)。这里表达一下个人的看法。首先我们需要了解的是灰度图中0是黑色,255是白色。我们通过核函数也就是滤波器来完成原图到特征图的转化,经过滤波器我们把这这个像素点和周围点的关系反映到特征图中。
首先分析边缘检测,假如你不是位于像素区域边缘的话,你和周围的像素值的差距理论上很小,在经过边缘检测的核函数之后这个点的像素值就会接近0(因为采用的是8相邻,中间的数字正好是8,周围是-1),就会变黑,自然边缘白色的部分就显示出来了。
接着是锐化,我们可以看到锐化的核函数和边缘检测的核函数是很相似的,只不过锐化采用的是四相邻,中间的数值是5,所以说即使这个像素点的值和周围的像素点的值相差不多,它还是会保持自己原本的状态,所以并不会出现很多黑色的部分,只是简单的突出了一下自己。
最后分析一下模糊,我们可以发现核函数里面的数加起来正好是1,如果某张图片的像素值都非常接近的话,那么很明显,经过模糊处理之后不会发生任何改变。模糊过程会把你和周围像素的差距进行分摊,使你和周围的像素差没有那么明显,当然还是以你自己为主,毕竟中间的值最大,从而达到了模糊的效果。
三、编程实现:
1.经典卷积核
代码实现
import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F
from PIL import Image
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 #有中文出现的情况,需要u'内容
# https://blog.csdn.net/weixin_40123108/article/details/83510592
file_path = 'pic.jpeg'
im = Image.open(file_path).convert('L') # 读入一张灰度图的图片
im = np.array(im, dtype='float32') # 将其转换为一个矩阵
print(im.shape[0], im.shape[1])
plt.imshow(im.astype('uint8'), cmap='gray') # 可视化图片
plt.title('原图')
plt.show()
im = torch.from_numpy(im.reshape((1, 1, im.shape[0], im.shape[1])))
conv1 = nn.Conv2d(1, 1, 3, bias=False) # 定义卷积
sobel_kernel = np.array([[-1, -1, -1],
[-1, 1, -1],
[-1, -1, -1]], dtype='float32') # 定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1, 1, 3, 3)) # 适配卷积的输入输出
conv1.weight.data = torch.from_numpy(sobel_kernel) # 给卷积的 kernel 赋值
edge1 = conv1(Variable(im)) # 作用在图片上
x = edge1.data.squeeze().numpy()
print(x.shape) # 输出大小
plt.imshow(x, cmap='gray')
plt.show()
就利用上面的核函数的参数进行处理。
原图
边缘检测
锐化
模糊
2.更换卷积核参数
经典的核函数虽然说有特定的作用,但是对不同的图片来说,同一个核函数很可能效果都不一样。所以想要达到想要的效果还是要根据需求变一下的。
上一个边缘检测黑白对比显然不是很明显,我们试着把中心点的8改成1
再试一下把8改成16,发现增大之后越来越接近原图了。
分析可以发现如果把8变成16就会变得更“白”,确实接近原图。因为电脑运行例子的过程实在太缓慢,这里不过多展示。
3.更换不同尺寸的图片
原图
边缘检测
不同尺寸的图片,观察不到明显区别。
4.更多卷积核
直接用网站测试测试网站
这个测试网站时非常便捷的,非常适合观察卷积核参数改变的过程中特征图的变化情况,更加深刻的理解卷积这个过程,强烈推荐去尝试一下。
1、bottom sobel 底部边缘检测
可以看到这个卷积核,顶部和底部像素差别大的地方突出显示。效果很不错。
2、left sobel 左部边缘加测,可以看到和底部检测还是有不同的
3、identity 一致,从卷积核函数就可以看出来,就是原图
4、emboss 浮雕
这个卷积核很有意思,对角线对称取相反数,可能时图片的问题,效果不是很明显。
5.彩色图片边缘检测
import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F
from PIL import Image
import matplotlib.pyplot as plt
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 #有中文出现的情况,需要u'内容
# https://blog.csdn.net/weixin_40123108/article/details/83510592
file_path = 'p.png'
im = Image.open(file_path) # 读入一张的图片
im = np.array(im, dtype='float32')
print(im.shape[0], im.shape[1])
im=np.transpose(im,(2,1,0))
im=im[np.newaxis,:]
conv1 = nn.Conv2d(3, 3, 3, bias=False) # 定义卷积
sobel_kernel = np.array([[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]], dtype='float32')/3 # 定义轮廓检测算子
sobel_kernel = sobel_kernel.reshape((1, 1, 3, 3)) # 适配卷积的输入输出
sobel_kernel=np.repeat(sobel_kernel,3,axis=1)#数组,次数,维度
sobel_kernel=np.repeat(sobel_kernel,3,axis=0)
conv1.weight.data = torch.from_numpy(sobel_kernel) # 给卷积的 kernel 赋值
edge1 = conv1(Variable(torch.from_numpy(im))) # 作用在图片上
x = edge1.data.squeeze().numpy()
print(x.shape) # 输出大小
x=np.transpose(x,(2,1,0))
plt.imshow(x, cmap='gray')
plt.title('彩色')
plt.show()
想要了解更多有关彩色图像处理的可以参考这里
总结
想要达到自己想要的图片效果,并不一定可以通过卷积固定函数实现,往往需要根据实际的情况改变合适的参数来达到自己想要的结果。
参考
卷积核实现的不同效果及其原理
第6章 Python 数字图像处理(DIP) - 彩色图像处理3
【2021-2022 春学期】人工智能-作业4:CNN - 卷积