1D Ranged Access¶
eAccessRanged1D
specifies that for every output position, a kernel needs access to a one-dimensional range of positions from a given image. Specifying the range over which an image will be accessed allows for optimisations that come from knowing the exact range of access that a kernel will need, by allowing for faster access over spaces where the pixel position and the specified range offset remain within the bounds of the image.
For example, by specifying that an output pixel will only need access to the input image from the adjacent neighbouring pixel positions, for most output pixels we do not have to worry about accessing outside of the image and can access those pixels fast, while only boundary pixels would require safe access with checks.
The BoxBlur1DKernel¶
This one-dimensional box blur demonstrates 1D ranged access.
// Copyright (c) 2024 The Foundry Visionmongers Ltd. All Rights Reserved.
kernel BoxBlur1D : public ImageComputationKernel<eComponentWise>
{
Image<eRead, eAccessRanged1D, eEdgeClamped> src;
Image<eWrite, eAccessPoint> dst;
param:
int radius; //The radius of the box blur
local:
int filterWidth;
void define() {
defineParam(radius, "Radius", 5, eParamProxyScale);
}
void init() {
// Set the range we need to access from the source
src.setRange(-radius, radius);
// Set the axis for the 1D-range to be horizontal
src.setAxis(eX);
filterWidth = 2 * radius + 1;
}
void process() {
// Sum all the pixel values within radius
float sum = 0.0f;
for(int i = -radius; i <= radius; ++i) {
sum += src(i);
}
// Write out the average value
dst() = sum / filterWidth;
}
};
Configuring 1D ranged image access¶
Image Specification¶
First, an image specification must use eAccessRanged1D
to allow for access to a one-dimensional range of pixels. The desired behaviour for accessing outside of image edges is also required, either eEdgeClamped
or eEdgeConstant
.
The example BoxBlur1DKernel requires one-dimensional access to its source image, specified as follows:
Image<eRead, eAccessRanged1D, eEdgeClamped> src;
Setting the Range¶
In order to use eRanged1D
, you must first define the size of the desired range of access, and whether the desired range is vertical or horizontal. These must be set up inside the init()
function using the following methods.
setAxis(Axis axis)
axis
parameter is eithereX
(horizontal) oreY
(vertical). Defines the ranged access axis for 1D ranged images.setRange(int rangeMin, int rangeMax)
This function sets the minimum and maximum extent of the range. For example,
setRange(-2, 2)
would allow accessing the 5 pixels in the inclusive range.
BoxBlur1DKernel example¶
In the BoxBlur1DKernel example, the range depends on the value of the radius
parameter, which is the blur size. The range passed to setRange(...)
is taken to be inclusive on both sides.
A call to setAxis(...)
sets the desired axis to access. In this case the blur is horizontal, so the axis is set to eX
. This will allow the kernel to access positions from (x - radius, y)
to (x + radius, y)
inclusive.
void init() {
// Set the range we need to access from the source
src.setRange(-radius, radius);
// Set the axis for the 1D-range to be horizontal
src.setAxis(eX);
Accessing the 1D Range¶
Pixel positions are accessed using relative offsets from the current pixel position. For example, this kernel will allow access the pixels adjacent to the current pixel position being processed:
kernel RangedAccess1DReadExample : ImageComputationKernel<eComponentWise>
{
Image<eRead, eAccessRanged1D, eEdgeClamped> src;
Image<eWrite> dst;
void init() {
src.setRange(-1, 1);
src.setAxis(eX);
}
void process() {
float sum = 0;
// Read the left adjacent input pixel
sum += src(-1);
// Read the current position input pixel
sum += src(0);
// Read the right adjacent input pixel
sum += src(1);
// output the average
dst() = sum / 3.0f;
}
};
It is also possible to write to a 1D range of the output image. For example:
kernel RangedAccess1DWriteExample : ImageComputationKernel<eComponentWise>
{
Image<eRead> src;
Image<eWrite, eAccessRanged1D, eEdgeClamped> dst;
void init() {
dst.setRange(-1, 1);
dst.setAxis(eX);
}
void process(int2 pos) {
// Only perform the write if it will be within the image bounds
if (pos.x > 0) {
// Write to the pixel to the left of the current position
dst(-1) = src();
}
}
};
BoxBlur1DKernel example¶
The process function of the BoxBlur1DKernel loops over our horizontal range from left to right, accumulating contributions to the box blur, before dividing by the total number of contributions using the value of filterWidth
that was calculated in the init()
function.
void process() {
// Sum all the pixel values within radius
float sum = 0.0f;
for(int i = -radius; i <= radius; ++i) {
sum += src(i);
}
// Write out the average value
dst() = sum / filterWidth;
}