![]() |
Mali OpenCL SDK v1.1.0
|
The Sobel image filter is a simple convolution filter used primarily for edge detection algorithms.
One technique for doing edge detection on an image is to find the gradient of the image. Areas with large gradients correspond to areas of large change in colour/intensity of the image. These areas are typically edges.
If you convolve the two Sobel operators with an image, you get two outputs:
The gradients are then typically combined to give a total gradient image.
The Sobel operators are:
dX:
-1 0 1 -2 0 2 -1 0 1
dY:
1 2 1 0 0 0 -1 -2 -1
Convolution is well suited to parallelization. Each output pixel only depends on the constant input pixels, not on outputs from any of the other pixels. Therefore, each pixel can be calculated independently and simultaneously.
For more details see Wikipedia.
We have included a 512x512 input bitmap for use with this sample (to keep the size of the installer small). However, you are more likely to see performance improvements (when compared to C code running on a CPU) when larger images are used. There is some start-up overhead associated with using OpenCL. This overhead can outweigh the benefits of parallel processing when the input data sizes are small.
This sample has been coded to allow any input bitmap to be used. Simply change input.bmp in the assets directory of the sample to the input image of your choice. You will see larger calculation performance improvements when larger images are used.
It is important to note that we have not considered padding here. The output image is two pixels smaller in both dimensions. Because every output requires pixels around it, it is impossible to calculate the output for the edge pixels. In this example we are simply leaving the edge output pixels as the values they are initialized to.
Sometimes it can be desirable to have the size of the output-signal be the same as the size of the input-signal, in which case "padding" must be applied to the input to take into account for the fact that the filter-application, by its nature, reduces the size. Strategies for padding differ, but for images, a common choice is to repeat the boundary-values (i.e. the outmost set of pixels) on all sides or (in some cases) on just some sides.
We implement Sobel filtering on a single 8-bit channel for simplicity. To do Sobel filtering on RGB images you can run the sobel filter on each channel seperately and then combine the results.
In this sample we take an RGB image, convert it to a 8-bit luminance image and send it to the GPU.
Each Sobel calculation gives an output for the centre pixel of the mask. The output value of the centre pixel is the sum of the pixel values in a 3x3 grid around the pixel, multiplied by the Sobel mask. This can be split into three stages by doing the multiplication and summations for each row of the grid separately (the approach used in this sample).
Unless otherwise noted, all code snippets come from the OpenCL kernel found in sobel.cl.
Choosing the size of the kernel
We are using vector types in the kernel and so we are actually outputting 16 results per kernel. See below for more details of vectorising. The kernel applies the 3x3 Sobel filter to a 18x3 window in the input image to produce 16x1 results in the two ouput images representing the dx and dy components of the gradient. We adjust the pointers into the data to reflect this:
And when we enqueue the kernel in sobel.cpp, we reduce the worksize accordingly:
Loading the input data
Mali-T600 series GPUs have 128-bit vector registers and can do arithmetic on vector types. Therefore, we use OpenCL vectors to make more efficient use of the hardware, leading to higher performance.
Here we do vector loads from one row of the data:
Converting the data
On a Mali-T600 series GPU, expanding and contracting data types is a free operation. Here, we convert the data from 8-bits per pixel to 16-bits per pixel:
Doing the calculation
Then we carry out the calculation on 16 pixels. Each vector calculation can be done as a single operation on a Mali-T600 series GPU:
We do this for the other two rows, accumulating the results in dx and dy.
Storing the results
Finally we contract and store the data. We use a vector store to write out all 16 results at once:
Because the data is returned as two seperate gradient images we combine them on the CPU before writing them out as a bitmap.
From a command prompt in the root of the SDK, run:
This compiles the Sobel sample code and copies all the files it needs to run to the bin folder in the root directory of the SDK.
Navigate to the folder on the board and run the Sobel binary:
You should see output similar to:
An output image should be created on the board called output.bmp.
Find solutions for Common Issues.
For more information have a look at the code in sobel.cpp and sobel.cl.