convolve method

List<Float32List> convolve(
  1. List<Float32List> src,
  2. List<Float32List> kernel, [
  3. List<Float32List>? tgt
])

Convolve an array with a kernel.

List<Float32List> src The source array to convolve. A nonzero-sized rectangular array of numbers. List<Float32List> kernel The kernel array with which to convolve src. A nonzero-sized rectangular array of numbers smaller than src. List<Float32List>? tgt The target array into which the result of the convolution should be put. If not passed, a new array will be created. This is also the array that this function returns. It must be at least as large as src.

return List<Float32List> An array containing the result of the convolution.

Implementation

List<Float32List> convolve(List<Float32List> src, List<Float32List> kernel, [List<Float32List>? tgt]) {
  // src and kernel must be nonzero rectangular number arrays.
  if (src.isEmpty || kernel.isEmpty) return src;
  // Initialize tracking variables.
  int i = 0, // current src x-position
      j = 0, // current src y-position
      a = 0, // current kernel x-position
      b = 0, // current kernel y-position
      w = src.length, // src width
      l = src[0].length, // src length
      m = kernel.length, // kernel width
      n = kernel[0].length; // kernel length
  // If a target isn't passed, initialize it to an array the same size as src.
  tgt ??= List.filled(w, new Float32List(l));
  // The kernel is a rectangle smaller than the source. Hold it over the
  // source so that its top-left value sits over the target position. Then,
  // for each value in the kernel, multiply it by the value in the source
  // that it is sitting on top of. The target value at that position is the
  // sum of those products.
  // For each position in the source:
  for (i = 0; i < w; i++) {
    for (j = 0; j < l; j++) {
      double last = 0;
      tgt[i][j] = 0;
      // For each position in the kernel:
      for (a = 0; a < m; a++) {
        for (b = 0; b < n; b++) {
          // If we're along the right or bottom edges of the source,
          // parts of the kernel will fall outside of the source. In
          // that case, pretend the source value is the last valid
          // value we got from the source. This gives reasonable
          // results. The alternative is to drop the edges and end up
          // with a target smaller than the source. That is
          // unreasonable for some applications, so we let the caller
          // make that choice.
          //if (typeof src[i+a] != 'undefined' && typeof src[i+a][j+b] != 'undefined') {
            last = src[i+a][j+b];
          //}
          // Multiply the source and the kernel at this position.
          // The value at the target position is the sum of these
          // products.
          tgt[i][j] += last * kernel[a][b];
        }
      }
    }
  }
  return tgt;
}