作者也在github上公开了matlab源码。源码中的核心逻辑比较简单,主要包括8个卷积,一个求min()函数找到最小值索引和一个根据索引取值这三大块。可以发现,这三个函数都可以在目前的深度学习框架中找到,从而我们能够利用现有的深度学习框架,快速的对该代码进行加速。本文使用pytorch快速重现了SWF,从而使得该函数的速度以较低的成本得到较快的提升,并有利于将该功能集成到现有项目中。
代码重现逻辑也较为简单。卷积、求min和按索引取值这三大部分的重现方法分别如下:
(1)在卷积部分,作者首先生成了3个一维卷积核,通过两两搭配得到8个卷积核。之后对输入图像进行卷积,并减去原输入图像,得到结果为width*height*8的矩阵。对于卷积核来说,我们可以使用指定初始化值的方法,将numpy矩阵的值赋到卷积核中。对于卷积操作来说,pytorch的特征图维度的通道顺序为N*C*H*W。彩色图的通道顺序可以看做是1*3*H*W。为了模拟SWF的卷积过程,可以利用batch通道,将channel换到第一维,得到3*1*H*W的矩阵,送入普通卷积,得到3*8*H*W的卷积结果。
作者的matlab代码的卷积过程用的是两个一维的卷积核,从而减少计算量。在本次的pytorch复现中,实现了两种方案,分别是使用两次一维卷积核的方案和一次二维卷积核的方案。使用二维卷积核方案的好处是卷积只需要进行一次,计算密度大,同时能够将减原图过程合并到卷积中。使用一维卷积核方案的好处是计算量小。在不同输入参数下,这两种方案各有优劣。
在对齐过程中,由于matlab的conv2会在内部将卷积核旋转180度,所以此处在输入卷积核参数时,提前旋转180度,保证后续对齐过程的简便性。
(2)在求min部分,由于上一步得到的结果维度为3*8*H*W,所以只需要对齐求绝对值后,再在第二维上求min,得到最小值索引即可。
(3)第三步按索引取值需要使用pytorch的gather函数。官方解释为“沿给定轴dim,将输入索引张量index指定位置的值进行聚合”。其具体用法正好是此处需要的功能。将第一步得到的结果,第二步得到的索引输入到gather函数中,并指定dim=1,便得到了最终的滤波结果。
以下是论文中的效果(复现时需设置合适的参数):
在代码对齐部分,由于精度问题,会存在一些像素的滤波结果有差异。比如某个位置卷积后求min时,matlab中八个值为[0.00123, - 0.00124, ……],经过求绝对值、min和索引取值,得到0.00123;pytorch中八个值为[0.00123, - 0.00122, ……],经过求绝对值、min和索引取值,得到- 0.00122,差异较大。不过这种差异并不影响最终效果。
实际测试加速效果如下:
总结
本文通过将SWF的matlab源码转为pytorch源码,使得能够以较低的写代码成本,快速的调用GPU,最终实现了约6倍的加速。同时使得该函数能够更方便的嵌入到项目中,降低了理论实用成本。