import numpy as np
import matplotlib.pyplot as plt
引进 numpy 的目的是因为它提供了极为便利的数组和矩阵操作,而 matplotlib.pyplot 可以轻松实现图标绘制,这在机器学习或者是深度学习过程当中是很重要的,因为数据的可视化有助于理解算法和调试算法。
然后需要一张测试图片。
srcImg = plt.imread('../images/lena.jpg')
输入图片的尺寸是 512 x 512 x 3,512 就它的宽高,3 代表了 RGB 3 个颜色通道。
然后,构建一个 3 x 3 的卷积核。
test_kernel = np.array([[-1,-1,-1],
[-1,9,-1],
[-1,-1,-1]])
在示例代码中,我们卷积操作时,跨度为 1。根据前面介绍的公式,我们很容易根据输入图片矩阵去构建输出图片的图像矩阵。
def generate_dst(srcImg):
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):
dst = generate_dst(src)
print dst.shape
conv(src,dst,kernel,k_size)
return dst
src 代表输入图片,kernel 自然就是卷积核,k_size 就是卷积核的大小,这里为 3。
上面的代码构建了输出图片的数据结构,并在内部调用了conv()方法。
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
前面的理论知识,介绍过,卷积操作需要滑动卷积核重复进行。
最里面的嵌套表示,对每一个颜色通道都需要进行卷积操作。你可以想象一下输入图片分成了 3 份,每一份尺寸同原图片一样,他们的叠加形成了原图。
不然看出,核心方法是 _con_each()
def _con_each(src_block,kernel):
pixel_count = kernel.size;
pixel_sum = 0;
_src = src_block.flatten();
_kernel = kernel.flatten();
for i in range(pixel_count):
pixel_sum += _src[i]*_kernel[i];
return pixel_sum / pixel_count;
注意它的输入参数,src_block 代表的是从输入图片上截取下来的像素块。它的尺寸同卷积核一样。那它是怎么截取下来的呢?请看下面的代码
src[i:i+k_size,j:j+k_size,k]
src 是 numpy 中的 ndarray 对象,先前说了它极其方便对数组和矩阵进行操作,这行代码表示,从原数组中截取起始坐标为 (i,j),宽高都为 k_size 的数据块。
我们再看 _con_each()方法,它进行了逐元素相乘,累计相加的操作,最终的数值还要求平均。
但我们知道,RGB 模式中,数值的取值范围是 0 ~ 255,如果超出这个范围就应该截断,所以我们需要优化下程序。
def _con_each(src,kernel):
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
return value;
小于 0 时,像素值取 0,大于 255 时取 255,其它情况保持现值。
现在,我们卷积操作的函数也完成了,我们可以测试一下。
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()
最终结果如下图。
卷积效果取决于卷积核,它的大小不同,里面的数值不同,卷积后的效果就会不同的,大家可以自行设计不同的卷积核进行试验。
参考:https://blog.csdn.net/rocling/article/details/103831994