Accessing Images¶
Accessing image values can only be performed inside the process(...)
function. How the access behaves depends on both the kernel granularity and the
image specification.
Accessing images is done by calling the ()
operator on the image object. This returns a reference to the pixel or component data at the specified position in the image. This will allow reading data from eRead
images and writing data to eWrite
images.
kernel ImageAccessExample : ImageComputationKernel<eComponentWise>
{
Image<eRead> src;
Image<eWrite> dst;
void process()
{
// At the current position in the iteration space
// the value of dst is set to the value of src
dst() = src();
}
};
In an eComponentWise
kernel, or if a component is specified when accessing an image in an ePixelWise
kernel (described below), a reference to a single component will be accessed. For kernels in Nuke BlinkScript nodes, this will be a float
type.
In an ePixelWise
kernel, a reference to a vector containing the values for all components at the position will be accessed. For kernels in Nuke BlinkScript nodes, this will be a floatn
type, such as float2
, float3
or float4
.
Access Parameters¶
Depending on the image specification, different parameters will be available for the ()
accessor operator.
eAccessPoint
AccessThe simplest access method. Only allows for the current pixel position to be read from or written to. No parameters related to the image specification are needed.
floatn image();
eAccessRanged1D
AccessThe parameter is an integer offset along the chosen axis for the point in the image to be accessed. The offset is relative to the current position in the iteration space. More information can be found in the 1D Ranged Access section.
floatn image(int offset);
eAccessRanged2D
AccessThe parameters are integer offsets in the x- and y-axes respectively for the point in the image to be accessed. Both offsets are relative to the current position in the iteration space. More information can be found in the 2D Ranged Access section.
floatn image(int horizontalOffset, int verticalOffset);
eAccessRandom
AccessThe parameters are the x and y coordinates of the position in the image you wish to access. This will return the value at the absolute position
(x, y)
in the image, whatever the current position in the iteration space. More information can be found in the Random Access section.
floatn image(int x, int y);
- Component Access
Finally, with a kernel granularity of
ePixelWise
you can specify an extra parameter which is the index,c
, of the component you wish to access. As this returns a single component, it will always return afloat
.
/// eAccessPoint component access
float image(int component);
// eAccessRanged1D component access
float image(int offset, int component);
// eAccessRanged2D component access
float image(int horizontalOffset, int verticalOffset, int component);
// eAccessRandom component access
float image(int x, int y, int component);
Image Type Methods¶
When accessing images, it is useful for kernels to be able to work with images with different types or different numbers of channels. For example, to make a pixel-wise Saturation kernel work for both RGBA and RGB images, you would need to write two near-identical kernels - one version for the float4
RGBA case and one for the float3
RGB case. This duplication can be avoided using the following image type methods, which allow a kernel to be written once and be applied to images with different numbers of channels.
ValueType(image)
Provides the data type of the components in the given image. For example
float
,half
orint
. In BlinkScript nodes in Nuke, this will always befloat
.SampleType(image)
Provides the data type for the pixels in the given image. For example, if
ValueType(image)
isfloat
and there are three components in your image,SampleType(image)
will befloat3
.
Warning
Care must be taken when writing kernels to handle varying numbers of input and output channels. Running a kernel with more outputs than inputs can result in out-of-bounds access errors, which can crash Nuke.
The following kernels demonstrate how these methods can be used in kernels.
Pixel-wise¶
kernel TypeMethodsExample : ImageComputationKernel<ePixelWise>
{
Image<eRead> src;
Image<eWrite> dst;
void process()
{
// ValueType can be initialised from an input image component.
int component = 0;
ValueType(src) value = src(component);
//SampleType can be initialised from an input image.
SampleType(src) sample = src();
// SampleType variables can be assigned to output images.
SampleType(dst) sampleOut = SampleType(dst)(1.0f);
dst() = sampleOut;
// ValueType variables can be assigned to output images.
ValueType(dst) valueOut = ValueType(dst)(1.0f);
// Implicit conversion from type to typeN
dst() = valueOut;
}
};
Component-wise¶
kernel TypeMethodsExample : ImageComputationKernel<eComponentWise>
{
Image<eRead> src;
Image<eWrite> dst;
void process()
{
// ValueType variables can be initialised from an input image component.
ValueType(src) value = src();
// SampleType variables can be initialised from an input image component.
SampleType(src) sample = src();
// ValueType variables can be assigned to output images.
ValueType(dst) valueOut = ValueType(dst)(1.0f);
dst() = valueOut;
// INVALID: Cannot assign SampleType variables to component-wise outputs.
// SampleType(dst) sampleOut = SampleType(dst)(1.0f);
// No conversion from typeN to type
// dst() = sampleOut;
}
};
Bilinear Interpolation¶
The bilinear()
function can be used to access pixels or components at non-integer positions within an Image
. The function performs bilinear interpolation to estimate the appropriate value from the four pixels or components nearest to the requested position. In a pixel-wise kernel, this will return a floatn
value, while in a component-wise kernel the value will be float
.
floatn bilinear(Image img1, float x, float y)
Within a pixel-wise kernel you can also specify a component to perform bilinear interpolation at position (x, y)
on a single specified component.
float bilinear(Image img1, float x, float y, int component)
Resize kernel example¶
The Resize kernel demonstrates using bilinear to sample the source image.
// Copyright (c) 2024 The Foundry Visionmongers Ltd. All Rights Reserved.
kernel Resize : ImageComputationKernel<eComponentWise>
{
Image<eRead, eAccessRandom, eEdgeConstant> src; // Output will be black outside the original image
Image<eWrite, eAccessPoint> dst;
param:
float externalScale;
bool horizontal;
local:
float scale;
void define()
{
defineParam(externalScale, "Scale", 0.5f);
defineParam(horizontal, "Horizontal", true);
}
void init()
{
scale = 1.0f / externalScale; // invert the scale as we back-map from dst to src
}
void process(int2 pos)
{
// Work out the scaled src position.
const float xPos = (horizontal ? (float)pos.x * scale : (float)pos.x);
const float yPos = (horizontal ? pos.y : (float)pos.y * scale);
// Use a bilinear filter to get the value at that src position.
dst() = bilinear(src, xPos, yPos);
}
};