图像过滤器:OpenCL的结果与CPU版本不同

时间:2014-03-31 07:23:27

标签: c++ image-processing opencl

我正在尝试在OpenCL中编写一个简单的图像过滤器。滤镜应采用32bpp彩色图像(从System::Drawing::Bitmap获得LockBits(..)),将像素转换为灰度并应用3x3滤镜矩阵。生成的图像应该能够显示为8bpp Bitmap,即Format8bppIndexed

我有一个内核实际上某些东西,以及一个单线程的CPU解决方案,我认为应该做同样的事情。但是,问题是生成的图像 不同:OpenCL处理后的图像更亮,几乎全白,而CPU图像看起来还不错 - 几乎就像它只是转换为灰度一样。

这是CPU解决方案:

static float filter[] = { -1.0f, -1.0f, -1.0f, -1.0f, 9.0f, -1.0f, -1.0f, -1.0f, -1.0f };
static float filterNorm = 1.0f;

for (int y = 0; y < height; ++y) {
    for (int x = 0; x < width; ++x) {
        float gray = 0.0f;

        size_t ia = 0;
        for (int yi = -1; yi <= 1; ++yi) {
            for (int xi = -1; xi <= 1; ++xi) {
                int xx = x + xi;
                if (xx < 0) xx = 0;
                if (xx >= width) xx = width - 1;
                int yy = y + yi;
                if (yy < 0) yy = 0;
                if (yy >= height) yy = height - 1;
                size_t idx = 4 * (yy * width + xx);
                float r = ((float)inputData32bpp[idx + 0] / 255.0f);
                float g = ((float)inputData32bpp[idx + 1] / 255.0f);
                float b = ((float)inputData32bpp[idx + 2] / 255.0f);
                gray += (filter[ia] * ((r + g + b)/3.0f));
                ++ia;
            }
        }
        gray /= filterNorm;

        if (gray < 0.0f) gray = 0.0f;
        if (gray > 1.0f) gray = 1.0f;

        size_t idx8 = y * width + x;
        outputData8bpp[idx8] = (unsigned char)(gray * 255.0);
    }
}

我正在转换为float,因为我想实现类似于OpenCL内核的行为,由于图像格式(floats),该行为也适用于CL_UNORM_INT8。我知道通道顺序可能是BGR而不是RGB,但是在这里转换为灰度时无关紧要。

OpenCL主机代码是:

static cl::ImageFormat formatBGRA(CL_BGRA, CL_UNORM_INT8);
static cl::ImageFormat formatGray(CL_LUMINANCE, CL_UNORM_INT8);

cl_int err = 0;
cl::Image2D inputImage(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR,
    formatBGRA, width, height, 0U, inputData32bpp, &err);
cl::Image2D outputImage(context, CL_MEM_READ_WRITE,
    formatGray, width, height, 0U, NULL, &err);

cl::Kernel& imgKernel = kernels[1];
err = imgKernel.setArg(0, inputImage);
err = imgKernel.setArg(1, outputImage);

err = queue.enqueueNDRangeKernel(imgKernel, cl::NDRange(0, 0), cl::NDRange(width, height));

err = queue.enqueueReadImage(outputImage, true, cl::size_t<3>(), getRegion(width, height),
    width * sizeof(unsigned char), 0, outputData8bpp);

我正在使用C ++ OpenCL API,而不是C语言。主机代码 工作;我已经成功地使用了更简单的内核。现在,内核就是这样:

__constant sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE |
    CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;

__kernel void sharpening(__read_only image2d_t inputImg, __write_only image2d_t outputImg) {

    int2 coord0 = (int2)(get_global_id(0), get_global_id(1));
    float matrix[9] = { -1.0f, -1.0f, -1.0f, -1.0f, 9.0f, -1.0f, -1.0f, 1.0f, 1.0f };

    float gray = 0.0f;
    int k = 0;
    for (int y = -1; y <= 1; ++y) {
        for (int x = -1; x <= 1; ++x) {
            int2 coord = coord0 + (int2)(x, y);
            float4 color = read_imagef(inputImg, sampler, coord);
            gray += (matrix[k] * ((color.x + color.y + color.z) /  3.0f));
            ++k;
        }
    }

    gray = clamp(gray, 0.0f, 1.0f);
    write_imagef(outputImg, coord0, (float4)(gray, gray, gray, 1));
}

为什么这与CPU版本不一样?我想我现在还没有看到一个低级问题。我见过that question,这让我担心我在这里遇到类似的问题?

如果重要的话:我在Surface Pro 2上运行代码,即Intel HD Graphics。

抱歉这样一个长长的问题,希望有人可以帮忙!

2 个答案:

答案 0 :(得分:1)

好的,对不起,显然这只是一个愚蠢的错误:OpenCL内核中的过滤器矩阵是错误的,即与CPU版本不同,因此是相对的差异。

关于谣言仅关于32bpp,或关于整数/浮点问题:是的, 可能在OpenCL中具有8bpp灰度图像。格式必须为CL_UNORM_INT8,这意味着必须通过read_imagef读取像素。读取内核中的像素始终返回具有四个分量的向量这一事实 not 意味着图像始终为32bpp。它似乎在内部表现为,但也可以从8bpp灰度图像中提供或读取 - 我的问题中的代码证明了这一点。

答案 1 :(得分:0)

我认为主要问题可能位于outputData8bpp。也许你期望这是一个8位输出。但是你可能正在取出以L,L,L,1.0格式结构化的32位输出数据。