Image Objects¶
Image objects are how kernels interact with and process images within Blink. They are instantiated by declaring an image specification, and are usually the first things listed in a Blink kernel.
Image Specification¶
A kernel image specification takes the following form. The items specified in the angle brackets <> describe how the image will be used in the kernel.
Image<*ReadSpec*, *AccessPattern*, *EdgeMethod*> image;
- ReadSpec
- This describes how the data in the image can be accessed. The options are:
eReadRead-only access to the image data.eWriteWrite-only access to the image data.eReadWriteBoth read and write access to the image data.
Note
eReadWrite is not available when writing kernels for BlinkScript nodes in Nuke.
- AccessPattern
- This describes how the kernel will access pixels in the image within the iteration space.
eAccessPoint(Default). Access only the current position in the iteration space.eAccessRanged1DAccess a one-dimensional range of positions relative to the current position in the iteration space.eAccessRanged2DAccess a two-dimensional range of positions relative to the current position in the iteration space.eAccessRandomAccess any pixel in the iteration space using absolute pixel positions.
- EdgeMethod
- The edge method for an image defines the behaviour if a kernel function tries to access data outside the image bounds.
eEdgeNone(Default). Values are undefined outside the image bounds and no within-bounds checks will be done when you access the image. This is the most efficient access method to use when you do not require access outside the bounds, because of the lack of bounds checks.eEdgeClampedThe edge values will be repeated outside the image bounds.eEdgeConstantZero values will be returned outside the image bounds.
Sampling outside the image bounds with eEdgeClamped. Red border denotes the original image edges.¶
Sampling outside the image bounds with eEdgeConstant. Red border denotes the original image edges.¶
Image Properties¶
The following properties are available from an Image. More detail on the different variable types can be found here.
image.kMinfloattype. The minimum possible value for any component of the image data. Typically0.0f.image.kMaxfloattype. The maximum possible value for any component of the provided image data. Typically1.0f.image.kWhitePointfloattype. The minimum value for any component of the image data which is considered to be white. All values above this will be what are known as “super-whites”. A floating-point image will usually have a white point of1.0f, though other values are also valid.image.kCompsinttype. The number of components in the image. For example in an RBGA image, this will be4.image.kClampsbooltype. Whether the image data should be clamped or not. For example, floating point data can take any value and thereforeimage.kClampswill be false.image.boundsThe bounds of the image, used to find information such as the images width and height.
image.bounds.x1inttype. The left-most horizontal value within the bounds.
image.bounds.x2inttype. The right-most horizontal value within the bounds.
image.bounds.y1inttype. The top-most vertical value within the bounds.
image.bounds.y2inttype. The bottom-most vertical value within the bounds.
image.bounds.width()Returns
inttype. The width of the bounds.x2 - x1.
image.bounds.height()Returns
inttype. The height of the bounds.y2 - y1.
image.bounds.size()Returns
int2type. The width and height of the bounds as a vector.
Image variables can be used in the process() and init() kernel functions. For example:
kernel ImagePropertiesExample : ImageComputationKernel<eComponentWise>
{
Image<eRead, eAccessRandom, eEdgeConstant> src;
Image<eWrite> dst;
local:
float halfImgWidth;
float halfImgHeight;
void init() {
// Calculate half of the image bounds that will be used by all threads
halfImgWidth = (float)src.bounds.width() / 2.f;
halfImgHeight = (float)src.bounds.height() / 2.f;
}
void process(int3 pos) {
// Check that our component position is within the source image's components
if(pos.z < src.kComps) {
dst() = src(pos.x * 2 - halfImgWidth, pos.y * 2 - halfImgHeight);
}
else {
dst() = 0.f;
}
}
};
Using src.bounds in the init() method allows for expensive divide operations to be performed once and then used at every pixel position, optimising performance.
Using src.kComps makes sure that if the number of output components is greater than the number of available input components, for example if this kernel is run on a four channel RGBA dst image with a three channel RGB src image, then the kernel will not try and access data that is not there.