欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

图像处理之卷积模式及C++实现

程序员文章站 2022-07-06 11:20:23
...

图像处理之卷积模式及C++实现

1. 卷积的三种模式

深度学习框架中通常会实现三种不同的卷积模式,分别是 SAME、VALID、FULL。这三种模式的核心区别在于卷积核进行卷积操作的移动区域不同,进而导致输出的尺寸不同。我们以一个例子来看这三种模式的区别,输入图片的尺寸是5x5 ,卷积核尺寸是 3x3 ,stride 取 1。

1.1 FULL 模式

FULL 模式下卷积核从与输入有一个点的相交的地方就开始卷积。如下图所示,蓝框的位置就是卷积核第一个卷积的地方,灰色部分是为了卷积能够正常进行的 padding(一般填 0)。因此 FULL 模式下卷积核移动区域最大,卷积后输出的尺寸也最大。
图像处理之卷积模式及C++实现

1.2 VALID 模式

VALID 模式与 FULL 模式相反,在整个卷积核与输入重叠的地方才开始卷积操作,因此不需要 padding,输出的尺寸也最小
图像处理之卷积模式及C++实现

1.3 SAME 模式

SAME 模式是最常用的一种模式,SAME 的意思是卷积后输出的尺寸与输入尺寸保持一致(假定 stride 为 1)。通过将卷积核的中心与输入的第一个点进行对齐确定卷积核起始位置,然后补齐对应 padding 即可。如下图所示,可以看到卷积输出的尺寸与出入保持一致。
图像处理之卷积模式及C++实现
SAME 模式下当卷积核边长为偶数时,可以通过在其中一边增加多一行(列)padding,即不对称的 padding 实现输出尺寸与输入尺寸保持一致,如下图所示(卷积核尺寸为 [公式] )
图像处理之卷积模式及C++实现
以上三种模式区别在于卷积核进行卷积操作的移动区域不同,其实是确定了所需的 padding。各种模式 padding 计算如下

def get_padding(inputs, ks, mode="SAME"):
    """
    Return padding list in different modes.
    params: inputs (input array)
    params: ks (kernel size) [p, q]
    return: padding list [n,m,j,k]
    """
    pad = None
    if mode == "FULL":
        pad = [ks[0] - 1, ks[1] - 1, ks[0] - 1, ks[1] - 1]
    elif mode == "VALID":
        pad = [0, 0, 0, 0]
    elif mode == "SAME":
        pad = [(ks[0] - 1) // 2, (ks[1] - 1) // 2,
               (ks[0] - 1) // 2, (ks[1] - 1) // 2]
        if ks[0] % 2 == 0:
            pad[2] += 1
        if ks[1] % 2 == 0:
            pad[3] += 1
    else:
        print("Invalid mode")
    return pad

确定了输入尺寸、卷积核尺寸、padding 以及 stride,输出的尺寸就被确定下来,可以由以下公式计算。其中 S ′ S' S, S S S 分别是输出、输入尺寸, K K K 是卷积核尺寸, P 1 P_1 P1, P 2 P_2 P2 分别是两侧的 padding。
图像处理之卷积模式及C++实现

2. C++代码实现

图像处理之卷积模式及C++实现
卷积运算本质上就是在滤波器和输入数据的局部区域间做点积,最直观明了的方法就是用滑窗的方式,c++简单实现如下:

输入:imput[IC][IH][IW]
IC = input.channels
IH = input.height
IW = input.width

卷积核: kernel[KC1][KC2][KH][KW]
KC1 = OC
KC2 = IC
KH = kernel.height
KW = kernel.width

输出:output[OC][OH][OW]
OC = output.channels
OH = output.height
OW = output.width

其中,padding = VALID,stride=1,
OH = IH - KH + 1
OW = IW - KW + 1


for(int ch=0;ch<output.channels;ch++)
{
    for(int oh=0;oh<output.height;oh++)
    {
        for(int ow=0;ow<output.width;ow++)
        {
            float sum=0;
            for(int kc=0;kc<kernel.channels;kc++)
            {
                for(int kh=0;kh<kernel.height;kh++)
                {
                    for(int kw=0;kw<kernel.width;kw++)
                    {
                        sum += input[kc][oh+kh][ow+kw]*kernel[ch][kc][kh][kw];
                    }
                }
            }
            //if(bias) sum +=bias[]
            output[ch][oh][ow]=sum;
        }
    }
}

直接用滑窗的方法计算卷积,效率比较低,因此一般把卷积操作转换为矩阵乘法。这样可以高效的利用优化之后的矩阵乘法,具体可以参考Caffe中的im2col的实现

3. 最后

图像处理之卷积模式及C++实现