我正在尝试在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。
抱歉这样一个长长的问题,希望有人可以帮忙!
答案 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位输出数据。