文章目录
作业1
编程实现
-
图1使用卷积核 ( 1 − 1 ) \begin{pmatrix} 1 & -1 \end{pmatrix} (1−1),输出特征图
-
图1使用卷积核 ( 1 − 1 ) \begin{pmatrix} 1\\ -1\\ \end{pmatrix} (1−1),输出特征图
-
图2使用卷积核 ( 1 − 1 ) \begin{pmatrix} 1 & -1 \end{pmatrix} (1−1),输出特征图
-
图2使用卷积核 ( 1 − 1 ) \begin{pmatrix} 1\\ -1\\ \end{pmatrix} (1−1),输出特征图
代码如下: -
图3使用卷积核 ( 1 − 1 ) \begin{pmatrix} 1 & -1 \end{pmatrix} (1−1), ( 1 − 1 ) \begin{pmatrix} 1\\ -1\\ \end{pmatrix} (1−1), ( 1 − 1 − 1 1 ) \begin{pmatrix} 1 &-1 \\ -1&1 \end{pmatrix} (1−1−11) ,输出特征图
1,2代码如下:
import numpy as np
import torch
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
w1 = np.array([1, -1], dtype='float32').reshape([1, 1, 1, 2])
w2 = np.array([1, -1], dtype='float32').T.reshape([1, 1, 2, 1])
print(w2)
w1 = torch.Tensor(w1)
w2 = torch.Tensor(w2)
conv1 = torch.nn.Conv2d(1, 1, [1, 2])
conv1.weight = torch.nn.Parameter(w1)
conv2 = torch.nn.Conv2d(1, 1, [2, 1])
conv2.weight = torch.nn.Parameter(w2)# 创建图像
img = np.ones([7, 6], dtype='float32')
img[:, 3:] = 0.
img[:, :3] = 255.
x = img.reshape([1, 1, 7, 6])
x = torch.Tensor(x)
y1 = conv1(x).detach().numpy()
y2 = conv2(x).detach().numpy()
plt.subplot(131).set_title('图1')
plt.imshow(img,cmap='gray')
plt.subplot(132).set_title('图1使用卷积核为(1,-1)结果')
plt.imshow(y1.squeeze(),cmap='gray')
plt.subplot(133).set_title('图1使用卷积核为(1,-1)T结果')
plt.imshow(y2.squeeze(),cmap='gray')
plt.show()
运行结果:
3,4代码如下:
import numpy as np
import torch
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
w1 = np.array([1, -1], dtype='float32').reshape([1, 1, 1, 2])
w2 = np.array([1, -1], dtype='float32').T.reshape([1, 1, 2, 1])
print(w2)
w1 = torch.Tensor(w1)
w2 = torch.Tensor(w2)
conv1 = torch.nn.Conv2d(1, 1, [1, 2])
conv1.weight = torch.nn.Parameter(w1)
conv2 = torch.nn.Conv2d(1, 1, [2, 1])
conv2.weight = torch.nn.Parameter(w2)# 创建图像
img = np.ones([8, 8], dtype='float32')
img[:4, :4] = 0.
img[:4, 4:] = 255.
img[4:, :4] = 255.
img[4:, 4:] = 0.
x = img.reshape([1, 1, 8, 8])
x = torch.Tensor(x)
y1 = conv1(x).detach().numpy()
y2 = conv2(x).detach().numpy()
plt.subplot(131).set_title('图2')
plt.imshow(img, cmap='gray')
plt.subplot(132).set_title('图2使用卷积核为(1,-1)结果')
plt.imshow(y1.squeeze(), cmap='gray')
plt.subplot(133).set_title('图2使用卷积核为(1,-1)T结果')
plt.imshow(y2.squeeze(), cmap='gray')
plt.show()
运行结果:
5代码如下:
import numpy as np
import torch
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
w1 = np.array([1, -1], dtype='float32').reshape([1, 1, 1, 2])
w2 = np.array([1, -1], dtype='float32').T.reshape([1, 1, 2, 1])
w3 = np.array([[1,-1,-1,1]],dtype='float32').reshape([1,1,2,2])
print(w3)
w1 = torch.Tensor(w1)
w2 = torch.Tensor(w2)
w3 = torch.Tensor(w3)
conv1 = torch.nn.Conv2d(1, 1, [1, 2])
conv1.weight = torch.nn.Parameter(w1)
conv2 = torch.nn.Conv2d(1, 1, [2, 1])
conv2.weight = torch.nn.Parameter(w2)
conv3 = torch.nn.Conv2d(1,1,[2,2])
conv3.weight = torch.nn.Parameter(w3)
# 创建图像
img = np.ones([9, 9], dtype='float32')
for i in range(7):
img[i+1,i+1]=255.
img[i+1,7-i]=255.
x = img.reshape([1, 1, 9, 9])
x = torch.Tensor(x)
y1 = conv1(x).detach().numpy()
y2 = conv2(x).detach().numpy()
y3 = conv3(x).detach().numpy()
plt.subplot(221).set_title('图3')
plt.imshow(img, cmap='gray')
plt.subplot(222).set_title('图3使用卷积核为(1,-1)结果')
plt.imshow(y1.squeeze(),cmap='gray')
plt.subplot(223).set_title('图3使用卷积核为(1,-1)T结果')
plt.imshow(y2.squeeze(),cmap='gray')
plt.subplot(224).set_title('图3使用卷积核为[[1 -1],[-1 1]]结果')
plt.imshow(y3.squeeze(),cmap='gray')
plt.show()
运行结果:
作业2
一、概念
用自己的语言描述“卷积、卷积核、特征图、特征选择、步长、填充、感受野”。
- 卷积:通过函数提取一部分数据获得信息的操作。
- 卷积核:卷积操作使用的函数。
- 特征图:一个图像经过卷积操作后得到的具有图像特征的图。一张彩色图片,通常我们认为有R,G,B三个通道,每个通道通过卷积核进行运算就会产生一个个的特征图,也就是说当图像像素值经过卷积核后的到的东西就是特征图。通常,经过多少个卷积核过滤就会产生多少个特征图,也就是下图中的层数,同时也是该层的深度。
- 特征选择:从已有的M个特征中选取N个特征,从原始特征中选择出一些最有效特征,有点类似于数据预处理。
- 步长:每过几个像素点进行一次卷积,步长就是确定像素点的个数。通俗的就是平移滑动多少步。
- 填充:卷积后图像会比之前小,填充就是把小了的地方填充,使卷积后和卷积前的图像大小相同。
- 感受野:在卷积神经网络中,感受野的定义是卷积神经网络每一层输出的特征图上的像素点在输入图片上映射的区域大小。再通俗点的解释是,特征图上的一个点对应输入图上的区域。
二、探究不同卷积核的作用
1.不同数值的卷积核对于图像卷积的影响
参考:Image Kernels explained visually (setosa.io)
模糊:
边缘检测:
底部轮廓检测:
锐化:
2.不同大小的卷积核的影响:
大卷积核
- 优点:感受域范围大
- 举例:AlexNet、LeNet等网络都使用了比较大的卷积核,如5×5、11×11
- 缺点:参数量多:计算量大
小卷积核
- 优点:参数量少;计算量小;整合三个非线性激活层代替单一非线性激活层,增加模型判别能力
- 举例:VGG之后
- 缺点:感受域不足;深度堆叠卷积(也就是堆叠非线性激活),容易出现不可控的因素
3.不同形状的卷积核的影响:
如果我们将一个33的卷积核变成一个13的卷积核和一个3*1的卷积核,那么会有:
3*3卷积计算量:9×9 = 81次乘法
1 * 3卷积和3 * 1卷积的累加计算量:3×15+3×9 = 72次乘法
可以看出的是,13的卷积核和31的卷积核的运算是比3*3的运算速度快的。
三、编程实现
1.实现灰度图的边缘检测、锐化、模糊。(必做)
边缘检测代码如下:
import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
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 = 'cat.png'
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, 8, -1],
[-1, -1, -1]], dtype='float32') # 定义轮廓检测算子
print(sobel_kernel.shape)
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() # 这个就是
运行结果:
锐化代码如下:
#encoding:utf-8
#By:Eastmount CSDN 2021-07-19
import cv2
import numpy as np
import matplotlib.pyplot as plt
#读取图像
img = cv2.imread('cat.png')
lenna_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#高斯滤波
gaussianBlur = cv2.GaussianBlur(grayImage, (3,3), 0)
#阈值处理
ret, binary = cv2.threshold(gaussianBlur, 127, 255, cv2.THRESH_BINARY)
#Roberts算子
kernelx = np.array([[-1,0],[0,1]], dtype=int)
kernely = np.array([[0,-1],[1,0]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
#Prewitt算子
kernelx = np.array([[1,1,1],[0,0,0],[-1,-1,-1]], dtype=int)
kernely = np.array([[-1,0,1],[-1,0,1],[-1,0,1]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX,0.5,absY,0.5,0)
#Sobel算子
x = cv2.Sobel(binary, cv2.CV_16S, 1, 0)
y = cv2.Sobel(binary, cv2.CV_16S, 0, 1)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
#拉普拉斯算法
dst = cv2.Laplacian(binary, cv2.CV_16S, ksize = 3)
Laplacian = cv2.convertScaleAbs(dst)
#效果图
titles = ['Source Image', 'Binary Image', 'Roberts Image',
'Prewitt Image','Sobel Image', 'Laplacian Image']
images = [lenna_img, binary, Roberts, Prewitt, Sobel, Laplacian]
for i in np.arange(6):
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
#encoding:utf-8
#By:Eastmount CSDN 2021-07-19
import cv2
import numpy as np
import matplotlib.pyplot as plt
#读取图像
img = cv2.imread('qeer.jpeg')
lenna_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#灰度化处理图像
grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#高斯滤波
gaussianBlur = cv2.GaussianBlur(grayImage, (3,3), 0)
#阈值处理
ret, binary = cv2.threshold(gaussianBlur, 127, 255, cv2.THRESH_BINARY)
#Roberts算子
kernelx = np.array([[-1,0],[0,1]], dtype=int)
kernely = np.array([[0,-1],[1,0]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Roberts = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
#Prewitt算子
kernelx = np.array([[1,1,1],[0,0,0],[-1,-1,-1]], dtype=int)
kernely = np.array([[-1,0,1],[-1,0,1],[-1,0,1]], dtype=int)
x = cv2.filter2D(binary, cv2.CV_16S, kernelx)
y = cv2.filter2D(binary, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Prewitt = cv2.addWeighted(absX,0.5,absY,0.5,0)
#Sobel算子
x = cv2.Sobel(binary, cv2.CV_16S, 1, 0)
y = cv2.Sobel(binary, cv2.CV_16S, 0, 1)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
Sobel = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
#拉普拉斯算法
dst = cv2.Laplacian(binary, cv2.CV_16S, ksize = 3)
Laplacian = cv2.convertScaleAbs(dst)
#效果图
titles = ['Source Image', 'Binary Image', 'Roberts Image',
'Prewitt Image','Sobel Image', 'Laplacian Image']
images = [lenna_img, binary, Roberts, Prewitt, Sobel, Laplacian]
for i in np.arange(6):
plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
运行结果:
模糊:
均值模糊代码如下:
# 图像模糊处理
# 均值模糊 box blur
import cv2
import numpy as np
import matplotlib.pyplot as plt
if __name__ == "__main__":
image = cv2.imread('cat.png')
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 此为均值模糊
# (30,1)为一维卷积核,指在x,y方向偏移多少位
dst1 = cv2.blur(image, (30, 1))
# 此为中值模糊,常用于去除椒盐噪声
dst2 = cv2.medianBlur(image, 15)
# 自定义卷积核,执行模糊操作,也可定义执行锐化操作
kernel = np.ones([5, 5], np.float32) / 25
dst3 = cv2.filter2D(image, -1, kernel=kernel)
plt.subplot(2, 2, 1)
plt.imshow(image)
plt.axis('off')
plt.title('Offical')
plt.subplot(2, 2, 2)
plt.imshow(dst1)
plt.axis('off')
plt.title('Box blur')
plt.subplot(2, 2, 3)
plt.imshow(dst2)
plt.axis('off')
plt.title('median blur')
plt.subplot(2, 2, 4)
plt.imshow(dst3)
plt.axis('off')
plt.title('defined blur')
plt.show()
运行结果:
高斯模糊代码如下:
# 图像模糊处理
# 高斯模糊 gaussian blur
# 使用自编写高斯噪声及自编写高斯模糊函数与自带高斯函数作效果对比
import cv2
import numpy as np
import matplotlib.pyplot as plt
def clamp(pv):
if pv > 255:
return 255
if pv < 0:
return 0
else:
return pv
def gaussian_noise(image): # 加高斯噪声
h, w, c = image.shape
for row in range(h):
for col in range(w):
s = np.random.normal(0, 20, 3)
b = image[row, col, 0] # blue
g = image[row, col, 1] # green
r = image[row, col, 2] # red
image[row, col, 0] = clamp(b + s[0])
image[row, col, 1] = clamp(g + s[1])
image[row, col, 2] = clamp(r + s[2])
dst = cv2.GaussianBlur(image, (15, 15), 0) # 高斯模糊
return dst, image
if __name__ == "__main__":
src = cv2.imread('cat.png')
plt.subplot(2, 2, 1)
plt.imshow(src)
plt.axis('off')
plt.title('Offical')
output, noise = gaussian_noise(src)
cvdst = cv2.GaussianBlur(src, (15, 15), 0) # 高斯模糊
plt.subplot(2, 2, 2)
plt.imshow(noise)
plt.axis('off')
plt.title('Gaussian Noise')
plt.subplot(2, 2, 3)
plt.imshow(output)
plt.axis('off')
plt.title('Gaussian Blur')
plt.subplot(2, 2, 4)
plt.imshow(cvdst)
plt.axis('off')
plt.title('defined blur by opencv')
plt.show()
运行结果:
运动模糊代码如下:
# 图像模糊处理
# 运动模糊,亦称动态模糊,motion blur
# 运动模糊:由于相机和物体之间的相对运动造成的模糊
import numpy as np
import cv2
import matplotlib.pyplot as plt
def motion_blur(image, degree=12, angle=45):
image = np.array(image)
# 这里生成任意角度的运动模糊kernel的矩阵, degree越大,模糊程度越高
M = cv2.getRotationMatrix2D((degree / 2, degree / 2), angle, 1)
motion_blur_kernel = np.diag(np.ones(degree))
motion_blur_kernel = cv2.warpAffine(motion_blur_kernel, M, (degree, degree))
motion_blur_kernel = motion_blur_kernel / degree
blurred = cv2.filter2D(image, -1, motion_blur_kernel)
# convert to uint8
cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)
blurred = np.array(blurred, dtype=np.uint8)
return blurred
if __name__ == "__main__":
img = cv2.imread('cat.png')
dst = motion_blur(img)
plt.subplot(1, 2, 1)
plt.imshow(img)
plt.axis('off')
plt.title('Offical')
plt.subplot(1, 2, 2)
plt.imshow(dst)
plt.axis('off')
plt.title('Motion blur')
plt.show()
运行结果:
2.调整卷积核参数,测试并总结。(必做)
图像锐化和模糊已给出不同参数的结果。
所以仅需修改边缘检测卷积核的参数,观察效果。
步长为2:
步长为4:
总结:步长小,提取的特征会更全面,但同时可能造成计算量增大,甚至过拟合等问题。步长大,计算量会下降,但很有可能错失一些有用的特征。随着步长的变大,像素点逐渐减少,并且图像提取的边界越来越模糊。
padding为2:
padding为4:
总结:加入padding后图像提取的特征会更全面,不会遗漏太多信息。但同时可能造成计算量增大,甚至过拟合等问题。步长大,计算量会下降,但很有可能错失一些有用的特征。
3.使用不同尺寸图片,测试并总结。(必做)
换个图片试试吧。
原图:
边缘检测:
高斯模糊:
锐化:
总结:对不同的图片,使用相同模糊,锐化,边缘算子提取效果很良好,但是也有一些差异,可能是由于图片间的差距影响的。
4.探索更多类型卷积核。(选做)
上面的模糊用到了均值模糊、高斯模糊、运动模糊。
均值模糊:
# 此为均值模糊
# (30,1)为一维卷积核,指在x,y方向偏移多少位
dst1 = cv2.blur(image, (30, 1))
# 此为中值模糊,常用于去除椒盐噪声
dst2 = cv2.medianBlur(image, 15)
# 自定义卷积核,执行模糊操作,也可定义执行锐化操作
kernel = np.ones([5, 5], np.float32) / 25
dst3 = cv2.filter2D(image, -1, kernel=kernel)
高斯模糊:
cvdst = cv2.GaussianBlur(src, (15, 15), 0) # 高斯模糊
运动模糊:
# 这里生成任意角度的运动模糊kernel的矩阵, degree越大,模糊程度越高
M = cv2.getRotationMatrix2D((degree / 2, degree / 2), angle, 1)
motion_blur_kernel = np.diag(np.ones(degree))
motion_blur_kernel = cv2.warpAffine(motion_blur_kernel, M, (degree, degree))
motion_blur_kernel = motion_blur_kernel / degree
blurred = cv2.filter2D(image, -1, motion_blur_kernel)
# convert to uint8
cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)
运行结果见上面。
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
im = Image.open('cat.png').convert('L') # 读入一张灰度图的图片
im = np.array(im, dtype='float32') # 将其转换为一个矩阵
#print(im.shape[0],im.shape[1]) 448*448
# 可视化图片
plt.imshow(im.astype('uint8'), cmap='gray')
im = torch.from_numpy(im.reshape((1, 1, im.shape[0], im.shape[1])))
conv1 = nn.Conv2d(in_channels=1,out_channels=1, kernel_size=3, bias=False,stride=1,padding=1) # 定义卷积
sobel_kernel = np.array([[-1, -1, -1], [-1, 8, -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)) # 作用在图片上
edge1 = edge1.data.squeeze().numpy() # 将输出转换为图片的格式
plt.imshow(edge1, cmap='gray')
plt.show()
运行结果:
总结
本次作业的心得体会,重点谈谈卷积能够提取特征的原理。
我们有一个方阵(n x n),总能找到一些向量,这个矩阵跟这些向量的作用效果,只是对这个向量造成了拉伸的作用,换句话说,矩阵的作用跟一个系数的作用是等价的。也就是如下这个式子:
那为什么要做这一步操作呢
我们换个通俗易懂的说法。假设我们要判断一个人是男的还是女的,第一反应可能是“头发”,其次是“声音”,或者“衣着”之类的,一般通过以上3个特征就能非常直观地判断出来是男的还是女的。
但是呢,出题人A特别坏,出题人A说:这个人,他有一双明亮的大眼睛,有一头乌黑的头发,喜欢出入酒吧,一般十点钟上班,声音比较细腻,走路比较急,还有,喜欢吃零食,喜欢穿浅色衣服。
出题人B比较好,出题人B说:他头发比较长,说话比较温柔,穿的比较阳光,声线比较细。
我们一听B的说法,就很直观地觉得这个人八九不离十是位女生,而从A的判断中,我们还是模棱两个。这个就涉及到信息量的问题。A给的特征非常多,信息量很大,但是。。。似乎没什么用,而B给的特征少,但是基本足够了。就像图1,他的数据分布特别散漫,而图3的分布相对集中,是一个道理的。相对于信息量大的特征,我们去挑选特征、做出判断是非常困难的。
所以,我们就需要想办法对特征做特征提取。也就是说,提取主要的、关键的特征就够了,而这个提取的方法就是:特征向量!
以椭圆举例:
以上几个图,全都可以分类为椭圆,但是因为形状各异,导致数据也是不对称的,尤其体现在rgb值上(当然在对图像的数据处理,还有很多,比如subtraction,BN),其次体现在在各个轴的投影。但是,如果如果我们可以对它做旋转,缩放,平移等操作,变成如下的图:
那就非常好辨认了,而且数据也非常集中,至少在某一维度上。于是,我们就需要去找这么一个特征向量。卷积的过程,就是通过反向传播,无限去拟合这么一个非常非常逼近的特征向量集(这个特征向量集其实就是卷积核)!为什么是特征向量集呢(其实单个特征向量(一个列向量或者行向量)也行)?因为一个列向量,我们只能在一种维度做变换,多个列向量,就意味着多个维度联合进行特征提取或者曰之为特征映射。
总结一下。卷积核≈特征向量集,反向传播≈求解特征向量集,我们的图片≈矩阵A,注意,这些概念不是等价的,只是用易懂的方式去解释这些原理。
卷积神经网络中,第一步一般用卷积核去提取特征,这些初始化的卷积核会在反向传播的过程中,在迭代中被一次又一次的更新,无限地逼近我们的真实解。其实本质没有对图像矩阵求解,而是初始化了一个符合某种分布的特征向量集,然后在反向传播中无限更新这个特征集,让它能无限逼近数学中的那个概念上的特征向量,以致于我们能用特征向量的数学方法对矩阵进行特征提取。
参考代码
import numpy as np
import torch
from torch import nn
from torch.autograd import Variable
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 = 'deer.jpg'
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, 8, -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()
参考链接
【2021-2022 春学期】人工智能-作业4:CNN - 卷积
6.2. 图像卷积 — 动手学深度学习 2.0.0-beta1 documentation (d2l.ai)
卷积神经网络工作原理的直观理解
浅析卷积神经网络为何能够进行特征提取
卷积核的类型及其作用
python 图像模糊处理实现
Python中的图像处理(第十一章)Python图像锐化及边缘检测(1)
实验四 图像频域平滑与锐化(Python实现)