首页 > 编程知识 正文

数字图像处理卷积运算,卷积代码实现

时间:2023-05-03 20:51:07 阅读:128294 作者:3652

问题解决传统的卷积运算是将卷积内核在窗口滑动的输入图上滑动,再乘以当前窗口中相应的元素进行求和,得到结果,每个窗口得到结果。相乘然后求和恰好也是向量内积的计算方式,因此可以将每个窗口中的元素引向向量。 如果按向量内积进行运算,则多个窗口中的向量合并为矩阵,每个卷积核心也是向量,而多个卷积核心中的向量排列为矩阵。 因此,卷积运算被转换为矩阵乘法。 下图很好地展示了矩阵乘法的运算过程。

如果将卷积运算变换为矩阵乘法,则乘法运算和加法运算次数没有太大差异,但如果变换为矩阵,则运算所需的数据存在于连续的存储器上,因此访问速度大幅提高(cache ),且矩阵乘法中有很多BLAS和MKL等有效的实现方法

有缺点吗? 这是一种改变数据在占用和转换更多内存——的过程中冗馀存储时间的方法。

有两张图像是为了帮助理解:

代码实现在此使用python的numpy实现。

一、实现滑动窗口版本

#! 将此语句添加到/usr/wwdkl/env python3 #中时,在终端命令行模式中直接输入此文件的名称,文件中的代码# _ * _ coding=utf-8 _ * _ importnumpyang 为了简化运算,缺省batch _ size=1class my _ conv (object ) :def_init_(self,input_data,weight_data,ssight ) padding='SAME ' ) : self.input=NP.as array (input _ data, np.float32 ) self.weights=NP.asarray ) weight np.float32 ) self.stride=stride self.padding=paddingdefmy _ cont w ''' [c,h,w]=self.input.shape [kc,k,_]=self.weights.shape #其中默认卷积内核的纵横比为assert c==kc # 如果输入的通道与卷积内核的通道不匹配,则output=[] #按通道进行卷积将出错。 最后forIinrange(c ) : f_map=self.input [ I ] kernel=self.weights [ I ] RS=self.com pute _ conv ) f _ map ) kernel (if output==[ ] : output=RS else : output=rsreturnoutputdefcompute _ conv (self,fm, kernel ) : [ h _ ]=kernel.shapeifself.padding==' same ' : pad _ h=(self.stride * (h-1 ) k - h )/2pad_w=) self 2rs _ h=hrs _ w=welifself.padding==' valid ' : pad _ h=0pad _ w=0rs _ h=(h-k )/self.stride1RS self.stride1elif self.padding==' full ' : pad _ h=k-1 pad _ w=k-1rs _ h=(H2 * pad _ h-) pad_h,w 2 * pad_w],np.float32 ) padding_FM[pad_h336666]

rs = np.zeros([rs_h, rs_w], np.float32) for i in range(rs_h): for j in range(rs_w): roi = padding_fm[i*self.stride:(i*self.stride + k), j*self.stride:(j*self.stride + k)] rs[i, j] = np.sum(roi * kernel) # np.asarray格式下的 * 是对应元素相乘 return rsif __name__=='__main__': input_data = [ [ [1, 0, 1, 2, 1], [0, 2, 1, 0, 1], [1, 1, 0, 2, 0], [2, 2, 1, 1, 0], [2, 0, 1, 2, 0], ], [ [2, 0, 2, 1, 1], [0, 1, 0, 0, 2], [1, 0, 0, 2, 1], [1, 1, 2, 1, 0], [1, 0, 1, 1, 1], ], ] weight_data = [ [ [1, 0, 1], [-1, 1, 0], [0, -1, 0], ], [ [-1, 0, 1], [0, 0, 1], [1, 1, 1], ] ] conv = my_conv(input_data, weight_data, 1, 'SAME') print(conv.my_conv2d())

二、矩阵乘法版本实现

#!/usr/wwdkl/env python3 #加上这一句之后,在终端命令行模式下就可以直接输入这个文件的名字后运行文件中的代码# _*_ coding = utf-8 _*_import numpy as np# 为了简化运算,默认batch_size = 1class my_conv(object): def __init__(self, input_data, weight_data, stride, padding = 'SAME'): self.input = np.asarray(input_data, np.float32) self.weights = np.asarray(weight_data, np.float32) self.stride = stride self.padding = padding def my_conv2d(self): """ self.input: c * h * w # 输入的数据格式 self.weights: c * h * w """ [c, h, w] = self.input.shape [kc, k, _] = self.weights.shape # 这里默认卷积核的长宽相等 assert c == kc # 如果输入的channel与卷积核的channel不一致即报错 # rs_h与rs_w为最后输出的feature map的高与宽 if self.padding == 'SAME': pad_h = (self.stride * (h - 1) + k - h) // 2 pad_w = (self.stride * (w - 1) + k - w) // 2 rs_h = h rs_w = w elif self.padding == 'VALID': pad_h = 0 pad_w = 0 rs_h = (h - k) // self.stride + 1 rs_w = (w - k) // self.stride + 1 elif self.padding == 'FULL': pad_h = k - 1 pad_w = k - 1 rs_h = (h + 2 * pad_h - k) // self.stride + 1 rs_w = (w + 2 * pad_w - k) // self.stride + 1 # 对输入进行zeros padding,注意padding后依然是三维的 pad_fm = np.zeros([c, h+2*pad_h, w+2*pad_w], np.float32) for i in range(c): pad_fm[i, pad_h:pad_h+h, pad_w:pad_w+w] = self.input[i] # 将输入和卷积核转化为矩阵相乘的规格 mat_fm = np.zeros([rs_h*rs_w, kc*k*k], np.float32) mat_kernel = self.weights mat_kernel.shape = (kc*k*k, 1) # 转化为列向量 row = 0 for i in range(rs_h): for j in range(rs_w): roi = pad_fm[:, i*self.stride:(i*self.stride+k), j*self.stride:(j*self.stride+k)] mat_fm[row] = roi.flatten() # 将roi扁平化,即变为行向量 row += 1 # 卷积的矩阵乘法实现 rs = np.dot(mat_fm, mat_kernel).reshape(rs_h, rs_w) return rsif __name__=='__main__': input_data = [ [ [1, 0, 1, 2, 1], [0, 2, 1, 0, 1], [1, 1, 0, 2, 0], [2, 2, 1, 1, 0], [2, 0, 1, 2, 0], ], [ [2, 0, 2, 1, 1], [0, 1, 0, 0, 2], [1, 0, 0, 2, 1], [1, 1, 2, 1, 0], [1, 0, 1, 1, 1], ], ] weight_data = [ [ [1, 0, 1], [-1, 1, 0], [0, -1, 0], ], [ [-1, 0, 1], [0, 0, 1], [1, 1, 1], ] ] conv = my_conv(input_data, weight_data, 1, 'SAME') print(conv.my_conv2d())

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。