2D Ranged Access¶
The BoxBlur2DKernel¶
The next kernel we’re going to look at also does a box blur, but this time in two dimensions. Here it is:
kernel BoxBlur2D : public ImageComputationKernel<eComponentWise>
{
Image<eRead, eAccessRanged2D, eEdgeClamped> src;
Image<eWrite, eAccessPoint> dst;
param:
int xRadius; //The horizontal radius of our box blur
int yRadius; //The vertical radius of our box blur
local:
int _filterSize;
void define() {
defineParam(xRadius, "RadiusX", 5, eParamProxyScale);
defineParam(yRadius, "RadiusY", 3, eParamProxyScale);
}
void init() {
//Set the range we need to access from the source
src.setRange(-xRadius, -yRadius, xRadius, yRadius);
_filterSize = (2 * xRadius + 1) * (2 * yRadius + 1);
}
void process() {
float sum = 0.0f;
for(int j = -yRadius; j <= yRadius; j++)
for(int i = -xRadius; i <= xRadius; i++)
sum += src(i, j);
dst() = sum / (float)_filterSize;
}
};
2D Ranged Access¶
This kernel needs to access a two-dimensional range of pixels from its source image. The source image is specified as follows:
Image<eRead, eAccessRanged2D, eEdgeClamped> src;
Here, the eAccessRanged2D tells us that for every output position, the BoxBlur2DKernel needs to access a two-dimensional range of positions from its input, src. As with 1D ranged access, the eEdgeClamped specifies what we want to happen if part of that 2D range falls outside the input image. Alternatively we could have specified eEdgeConstant or eEdgeNone for the edge method here, as before.
You could also write to a 2D range of the output image by specifying eAccessRanged2D access on it, though it’s not required for this example.
Setting up the 2D Range¶
To set up the 2D range, we again call setRange() inside the init() function. This time, we give it four parameters to specify the horizontal minimum, vertical minimum, horizontal maximum and vertical maximum respectively:
void init() {
//Set the range we need to access from the source
src.setRange(-xRadius, -yRadius, xRadius, yRadius);
If the range is the same for each dimension, (i.e. xRadius == yRadius) then the shorthand setRange function with two parameters may be used:
src.setRange(-radius, radius)
All ranges are taken to be inclusive on both sides.
Since we’re now using a 2D range which covers both axes, there’s no need to call setAxis() this time.
Accessing the 2D Range¶
Access to our 2D range from inside the proccess() function is very similar to accessing the 1D range, except that we now need to specify two offsets from the current position instead of one.
This process() function loops over the vertical range of our box blur. For each vertical position, it loops over the horizontal range, accumulating contributions to our box blur from each source position in turn:
void process() {
float sum = 0.0f;
for(int j = -yRadius; j <= yRadius; j++)
for(int i = -xRadius; i <= xRadius; i++)
sum += src(i, j);
dst() = sum / (float)_filterSize;
}
In the final step, the output pixel is set to the average value of all the contributions, using the value of _filterSize we calculated inside the init() function.
Next Steps¶
The final type of image access is random access, which we’ll cover next.