将 3d 数组传递给 C 中的卷积函数

Passing 3d arrays to a convolution function in C

我需要执行一个执行 2D 卷积的函数,为此我需要将几个 3d 数组传递给它。然而,有人告诉我我的方法不是执行此操作的理想方法。

首先,我声明变量:

typedef struct {
    float img[224][224][3];
} input_224_t;

typedef struct {
    float img[112][112][32];
} input_112_t;

typedef struct {
    float img[3][3][32];
} weightsL1_t;

然后,卷积看起来像这样:

void convolution(input_224_t* N, weightsL1_t* M, input_112_t* P, int size, int ksize, int channels, int filters, int stride)
{
    // Effectively pads the image before convolution. Technically also works for pointwise, but it's inefficient.
    // find center position of kernel (half of kernel size)
    int kcenter = ksize / 2;

    // Declare output indexes
    int a = 0;
    int b = -1;

    for (int k = 0; k < filters; ++k)                   // filters
    {
        for (int i = 0; i < size; i = i + stride)       // rows
        {
            for (int j = 0; j < size; j = j + stride)   // columns
            {
                b++;
                if (b == ksize) {b=0;a++;}              // Increment output index
                for (int m = 0; m < ksize; ++m)         // kernel rows
                {
                    for (int n = 0; n < ksize; ++n)     // kernel columns
                    {
                        // Index of input signal, used for checking boundary
                        int ii = i + (m - kcenter);
                        int jj = j + (n - kcenter);

                        // Ignore input samples which are out of bound
                        if (ii >= 0 && ii < size && jj >= 0 && jj < size) {
                            for (int p = 0; p < channels; ++p)  // channels
                            {
                                P.img[a][b][k] += N.img[ii][jj][p] * M.img[m][n][k];    // convolve
                            }
                        }
                    }
                }
            }
        }
    }
}

(这个returns“字段'img'无法解析”在“卷积”行)

然后我将这些值导入到正确的结构中(这是我之前回答的问题:),我这样调用函数:

convolution(test_image, test_filter, test_result, 6, 3, 1, 1, 2);

我在上一个问题中被告知这不是处理 3D 数组的理想方式,并且它可能使用比我预期的更多的内存。这是一个非常占用内存的过程,在嵌入式系统中会 运行,因此优化内存分配至关重要。

我的objective,如果可能的话,是在任何时间点只分配这些 3D 数组中的每一个,而不是使用不必要的内存,并且做以某种方式 space 可以在以后释放。

提前谢谢你。

您可以使用可变长度数组作为函数参数。

void convolve(int isize,  // width/height of input (224)
              int osize,  // width/height of output (112)
              int ksize,  // width/height of kernel (3)
              int stride, // shift between input pixels, between consecutive outputs
              int pad,    // offset between (0,0) pixels between input and output
              int idepth, int odepth, // number of input and output channels
              float idata[isize][isize][idepth],
              float odata[osize][osize][odepth],
              float kdata[idepth][ksize][ksize][odepth])

{
  // iterate over the output
  for (int oy = 0; oy < osize; ++oy) {
  for (int ox = 0; ox < osize; ++ox) {
  for (int od = 0; od < odepth; ++od) {
      odata[oy][ox][od] = 0;
      for (int ky = 0; ky < ksize; ++ky) {
      for (int kx = 0; kx < ksize; ++kx) {
          // map position in output and kernel to the input
          int iy = stride * oy + ky - pad;
          int ix = stride * ox + kx - pad;
          // use only valid inputs
          if (iy >= 0 && iy < isize && ix >= 0 && ix < isize)
              for (int id = 0; id < idepth; ++id)
                  odata[oy][ox][od] += kdata[id][ky][kx][od] * idata[iy][ix][id];
      }}
  }}}
}

典型用法为:

// allocate input
float (*idata)[224][3] = calloc(224, sizeof *idata);
// fill input using idata[y][x][d] syntax

// allocate kernel
float (*kdata)[3][3][32] = calloc(3, sizeof *kdata);
// fill kernel

// allocate output
float (*odata)[112][32] = calloc(112, sizeof *odata);

convolve(224, 112, 3, // input, output, kernel size
         2, // stride
         1, // pad input by one pixel what will center the kernel
         3, 32, // number of input and output channels
         idata, odata, kdata);

// free memory if it is no longer used
free(idata); free(odata); free(kdata);

多维数组可以分配为:

float (*arr)[10][20][30] = malloc(sizeof *arr);

然而,由于语法 (*arr)[i][j][j],访问元素有点麻烦。因此,使用指向数组第一个元素的指针并在该指针处分配多个子数组很简单。

float (*arr)[20][30] = malloc(10 * sizeof *arr);

calloc() 自动归零并避免溢出。

float (*arr)[20][30] = calloc(10, sizeof *arr);

顺便说一句。我建议将内核的维度重新排序为 ODEPTH x KSIZE x KSIZE x IDEPTH。这将使对内核的迭代更加缓存友好。