Convolution

The Convolution kernel convolves its first input with a second, filter input. It has no parameters.

// Copyright (c) 2024 The Foundry Visionmongers Ltd.  All Rights Reserved.
// WARNING: This file is referenced within Images/RandomAccess.rst. Line number references will need updating if changes are made to this file.

/// Convolution kernel: Convolves the input image with a filter.
// Warning: connecting a large image to the filter input will cause the kernel to run very slowly!
// If running on a GPU connected to a display, this will cause problems if the time taken to 
// execute the kernel is longer than your operating system allows. Use with caution!
kernel ConvolutionKernel : public ImageComputationKernel<ePixelWise>
{
  Image<eRead, eAccessRanged2D, eEdgeClamped> src;
  Image<eRead, eAccessRandom> filter;  
  Image<eWrite> dst;

local:
  int2 filterOffset;

  void init()
  {
    // Get the size of the filter input and store the radius.
    int2 filterRadius(filter.bounds.width() / 2, filter.bounds.height() / 2);

    // Store the offset of the bottom-left corner of the filter image from the current pixel
    filterOffset.x = filter.bounds.x1 - filterRadius.x;
    filterOffset.y = filter.bounds.y1 - filterRadius.y;
    
    // Set up the access for the src image
    src.setRange(-filterRadius.x, -filterRadius.y, filterRadius.x, filterRadius.y);
  }

  void process() {

    SampleType(src) valueSum(0.f);
    ValueType(filter) filterSum(0.f);
    
    // Iterate over the filter image
    for(int j = filter.bounds.y1; j < filter.bounds.y2; j++) {
      for(int i = filter.bounds.x1; i < filter.bounds.x2; i++) { 
        // Get the filter value
        ValueType(filter) filterVal = filter(i, j, 0);

        // Multiply the src value by the corresponding filter weight and accumulate
        valueSum += filterVal * src(i + filterOffset.x, j + filterOffset.y);

        // Update the filter sum with the current filter value
        filterSum += filterVal;
      }
    }

    // Normalise the value sum, avoiding division by zero
    if (filterSum != 0.f) {
      valueSum /= filterSum;
    }
    
    dst() = valueSum;
  }
};