参考:【深度学习入门】——亲手实现图像卷积操作
基础知识见参考链接
直接记录编程实践部分:
二维卷积
import numpy as np
import matplotlib.pyplot as plt
输入测试图片:
srcImg = plt.imread('./lena.jpg')
构建一个 3 x 3 的卷积核:
test_kernel = np.array([[-1,-1,-1],
[-1,9,-1],
[-1,-1,-1]])
构建输出图片的图像矩阵函数:
def generate_dst(srcImg):
"""
srcImg:输入图像,512*512*3
输出图像矩阵计算公式:
l=(m−k)/stride+1
c=(n−k)/stride+1
stride =1
output:输出图片的图像矩阵
"""
m = srcImg.shape[0]
n = srcImg.shape[1]
n_channel = srcImg.shape[2]
dstImg = np.zeros((m-test_kernel.shape[0]+1,n-test_kernel.shape[0]+1,n_channel ))
return dstImg
构建输出图片的数据结构函数:
def conv_2d(src,kernel,k_size):
"""
二维卷积:构建输出图片的数据结构
src:输入图像
kernal:卷积核
k_size:卷积核大小,3
"""
dst = generate_dst(src)
print (dst.shape)
conv(src,dst,kernel,k_size)
return dst
构建卷积函数 :
def conv(src,dst,kernel,k_size):
"""
卷积:滑动卷积核重复
最里面的嵌套:对每一个颜色通道都需要进行卷积操作
"""
for i in range(dst.shape[0]):
for j in range(dst.shape[1]):
for k in range(dst.shape[2]):
value = _con_each(src[i:i+k_size,j:j+k_size,k],kernel)
dst[i,j,k] = value
使用sigmod激活函数对卷积后的数值进行操作 ,得到一个新的输出:
def sigmoid(x):
return 1 / (1 + np.exp(-x))
构建具体卷积操作函数:
def _con_each(src,kernel):
"""
逐元素相乘,累计相加的操作,最终的数值求平均
src_block:输入图片上截取下来的像素块,尺寸同卷积核一样
src[i:i+k_size,j:j+k_size,k] :从原数组中截取起始坐标为 (i,j),宽高都为 k_size 的数据块。
RGB 模式中,数值的取值范围是 0 ~ 255
小于 0 时,像素值取 0,大于 255 时取 255,其它情况保持现值
"""
pixel_count = kernel.size
pixel_sum = 0
_src = src.flatten()
_kernel = kernel.flatten()
for i in range(pixel_count):
pixel_sum += _src[i]*_kernel[i]
value = pixel_sum / pixel_count
value = value if value >0 else 0
value = value if value < 255 else 255
value = sigmoid(value)
return value
构建测试函数:
def test_conv(src,kernel,k_size):
plt.figure()
#121 1 代表 1行,2 代表 2 列,最后的 1 代表 图片显示在第一行第一列
plt.subplot(121)
plt.imshow(src)
dst = conv_2d(src,kernel,k_size)
#121 1 代表 1行,2 代表 2 列,最后的 2 代表 图片显示在第一行第发给列
plt.subplot(122)
plt.imshow(dst)
plt.show()
测试用例:
test_conv(srcImg,test_kernel,3)
输出结果:
三维卷积
import numpy as np
import matplotlib.pyplot as plt
def _conv_epoch(src_block,filter):
input = src_block.flatten()
filter = filter.flatten().T
return np.dot(input,filter) #矩阵内积先累乘再累加,与卷积的过程一样
构建完整的图像卷积函数:
def conv(img,input_size,filter_size,stride=1):
"""
完整的图像卷积
input_size:(h,w,c)
filter_size:(h,w,ic,oc)
"""
ih = input_size[0]
iw = input_size[1]
ic = input_size[2]
filter_h = filter_size[0]
filter_w = filter_size[1]
filter_ic = filter_size[2]
filter_oc = filter_size[3]
l = int((ih - filter_h) / stride + 1)
m = int((iw - filter_w) / stride + 1)
result = np.zeros(shape=(l,m,filter_oc),dtype=np.uint8)
for i in range(l):
for j in range(m):
for k in range(filter_oc):
f = np.random.uniform(0,1,filter_w*filter_h*filter_ic).T
input = img[i:i+filter_h,j:j+filter_w,:]
#if i == 1 and j == 1 and k == 1:
# print(f)
# print(input)
result[i,j,k] = _conv_epoch(input,f)
return result
三维卷积函数相当于把二维卷积里面的几个函数规整在一个函数中了。
测试用例:
def test():
img = plt.imread("./cat.jpg")
print("img shape ",img.shape)
result = conv(img,img.shape,(3,3,img.shape[2],3))
plt.figure()
plt.subplot(121)
plt.imshow(img)
plt.subplot(122)
plt.imshow(result)
plt.show()
test()
输出结果:
补充及总结:
明白啦!下班前向老师请教了一下!filter_size是四维数据,分别是filter_h、filter_w、filter_ic、filter_oc,前两者是滤波器的高度和宽度,第三个事滤波器的输入通道数,应该与上层图片的通道数相同,最后一个是滤波器的输出通道数,也就是滤波器的数量。
比如这个图是指三维卷积的滤波过程,三维卷积之后,有4个filter,所以在这里filter_oc=4