1

I've got a WPF - C# project and I'm trying to speed up the image processing I'm doing in there; by doing it using OpenCL and Cloo.

I did manage to get a kernel working that can change a bitmap to grayscale, but for some reason the black and white kernel I've made only outputs a completely black image.

Here's my kernel .cl code :

kernel void ConvertToBlackAndWhite(read_only image2d_t sourceImage, write_only image2d_t outputImage, float threshold)
{
    const sampler_t mainSampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR;

    int2 position = (int2)(get_global_id(0), get_global_id(1));
    float4 pixel = read_imagef(sourceImage, mainSampler, position);

    float grayValue = (0.299 * pixel.z) + (0.587 * pixel.y) + (0.114 * pixel.x); // Get Grayscale Value

    const float thresholdValue = threshold * 255.0;

    if (grayValue > thresholdValue)
    {
        const float4 white = (float4){ 255.0, 255.0, 255.0, 255.0 }; // #FFFFFF
        write_imagef(outputImage, position, white);
    }
    else
    {
        const float4 black = (float4){ 0.0, 0.0, 0.0, 255.0 }; // #000000
        write_imagef(outputImage, position, black);
    }
}

Where am I going wrong?

Any help or tips will be appreciated.

I've updated my kernel code based on @ProjectPhysX's helpful advice :

kernel void ConvertToBlackAndWhite(read_only image2d_t sourceImage, write_only image2d_t outputImage, float threshold)
{
    const sampler_t mainSampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR;

    int2 position = (int2)(get_global_id(0), get_global_id(1));
    float4 pixel = read_imagef(sourceImage, mainSampler, position);

    float grayValue = (0.299f * pixel.z) + (0.587f * pixel.y) + (0.114f * pixel.x); // Get Grayscale Value
    if (grayValue > threshold)
    {
        const float4 white = (float4){ 1.0f, 1.0f, 1.0f, 1.0f }; // #FFFFFF
        write_imagef(outputImage, position, white);
    }
    else
    {
        const float4 black = (float4){ 0.0f, 0.0f, 0.0f, 1.0f }; // #000000
        write_imagef(outputImage, position, black);
    }
}

Here's the C# that adds the images into OpenCL image memory buffers :

...

// Allocate OpenCL Image Memory Buffer
inputImage2DBuffer = Cl.CreateImage2D(GPUManipulation.OpenCLContext, MemFlags.CopyHostPtr | MemFlags.ReadOnly, imageFormat,
                                     (IntPtr)width, (IntPtr)height,
                                     (IntPtr)ConstantsMain.Int0, inputByteArray, out error);

...

// Allocate OpenCL image memory buffer
IMem outputImage2DBuffer = Cl.CreateImage2D(GPUManipulation.OpenCLContext, MemFlags.CopyHostPtr | MemFlags.WriteOnly, imageFormat,
                                            (IntPtr)width, (IntPtr)height, (IntPtr)ConstantsMain.Int0, outputByteArray, out error);

...
Shiasu-sama
  • 1,179
  • 2
  • 12
  • 39

1 Answers1

1

read_imagef returns color/alpha values in the range [0.0f, 1.0f] and not [0.0f, 255.0f]. This means you have to set:

const float thresholdValue = threshold; // do not multiply by 255.0f here
const float4 white = (float4){ 1.0f, 1.0f, 1.0f, 1.0f }; // #FFFFFF
const float4 black = (float4){ 0.0f, 0.0f, 0.0f, 1.0f }; // #000000

The reason everything was black before was that no grayValue (in the range [0.0f, 1.0f]) could possibly be larger than thresholdValue (in the order of 0.5f*255.0f).

ProjectPhysX
  • 4,535
  • 2
  • 14
  • 34
  • Thanks for the advice :). You can probably tell; I'm still a rookie. I've updated my kernel code above, but I'm still experiencing the same problem. I'm testing at a threshold of 0.5. – Shiasu-sama Feb 15 '21 at 14:47
  • Which flags do you use in `clCreateImage`? Does conversion from/to `image2d_t` work (if you don't run the kernel, will the output image be the original image)? – ProjectPhysX Feb 15 '21 at 16:56
  • For the input image i think it's `MemFlags.CopyHostPtr | MemFlags.ReadOnly` and for the output image it's `MemFlags.CopyHostPtr | MemFlags.WriteOnly` – Shiasu-sama Feb 15 '21 at 18:12
  • My C# is based off of this post : https://www.codeproject.com/Articles/502829/GPGPU-image-processing-basics-using-OpenCL-NET – Shiasu-sama Feb 15 '21 at 18:13
  • With flags I meant `Cl.ChannelOrder.RGBA, Cl.ChannelType.Unsigned_Int8`. These should be alright. Try not running the kernel and just the C# part. Is the output image then the same as the inoput image? – ProjectPhysX Feb 15 '21 at 20:05
  • Ok. I've run it with an empty kernel and the result was an all black image. Here's the `.cl` : `kernel void Empty(read_only image2d_t sourceImage, write_only image2d_t outputImage){}` – Shiasu-sama Feb 15 '21 at 21:00
  • I have now got a kernel that works. Instead of `read_imagef`; it uses : `read_imageui` then uses this to convert : `float4 pixelFloat = convert_float4(pixel) / 255.0f;`. I have no idea why that works for me, but the updated kernel above, that uses `read_imagef` doesn't. – Shiasu-sama Feb 15 '21 at 21:06