为什么OpenCL内核不使用普通的xy坐标和Image2D?

TLDR;

对于任何人在试图弄清楚如何用OpenCL做高斯模糊或灰度的时候到达这里,最后的工作代码就在这里 。 请注意,在该回购协议中,我实际上是使用Nvidia的Docker包装器在GPU内部运行Docker中的所有内容。 您可以在“Dockerfile”里面find需要运行代码的步骤,或者在Nvidia-Docker上运行,如果你有这个设置并且在Nvidia GPU上运行的话。

原问题:

在OpenCL图像filter应用程序中使用以下内核,我得到预期的结果,即返回的灰度版本的input图像:

const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST; __kernel void process(__read_only image2d_t src, __write_only image2d_t dst) { int x = get_global_id(0); int y = get_global_id(1); float4 color; color = read_imagef(src, sampler, (int2)(x, y)); float gray = (color.x + color.y + color.z) / 3; write_imagef(dst, (int2)(x,y), (float4)(gray, gray, gray, 0)); } 

到现在为止还挺好。 然后,我试图创build一个内核,只是复制图像的顶部和左边界:

 const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST; __kernel void process(__read_only image2d_t src, __write_only image2d_t dst) { int x = get_global_id(0); int y = get_global_id(1); float4 color; if (x < 10 || y < 10) { color = read_imagef(src, sampler, (int2)(x, y)); write_imagef(dst, (int2)(x,y), (float4)(color.x, color.y, color.z, 0)); } else { write_imagef(dst, (int2)(x,y), (float4)(0,0,0,0)); } } 

返回的图像不是我所期望的: 图片显示错误处理

我以这种方式加载input图像:

 // Load an image using the OpenCV library and create an OpenCL // image out of it cl::Image2D LoadImage(cl::Context context, char *fileName, int &width, int &height) { cv::Mat image = cv::imread(fileName, CV_LOAD_IMAGE_COLOR); cv::Mat imageRGBA; width = image.rows; height = image.cols; cv::cvtColor(image, imageRGBA, CV_RGB2RGBA); char *buffer = reinterpret_cast<char *>(imageRGBA.data); cl::Image2D clImage(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, cl::ImageFormat(CL_RGBA, CL_UNORM_INT8), width, height, 0, buffer); return clImage; } 

输出图像:

 cl::Image2D imageOutput(context, CL_MEM_WRITE_ONLY, cl::ImageFormat(CL_RGBA, CL_UNORM_INT8), width, height, 0, NULL); 

内核:

 cl::Program program(context, util::loadProgram("border.cl"), true); cl::make_kernel<cl::Image2D, cl::Image2D> filter(program, "process"); cl::NDRange global(width, height); filter(cl::EnqueueArgs(queue, global), clImageInput, imageOutput); 

然后读回图像:

 cl::size_t<3> origin; origin[0] = 0; origin[1] = 0, origin[2] = 0; cl::size_t<3> region; region[0] = width; region[1] = height; region[2] = 1; float* oup = new float[width * height]; queue.enqueueReadImage(imageOutput, CL_TRUE, origin, region, 0, 0, oup); cv::imwrite(filename_out, cv::Mat(width, height, CV_8UC4, oup)); 

为什么图像的处理方式是? 只selecty坐标小于10的像素似乎可以工作,但是selectx坐标小于10的像素似乎在图像上错开。

如果我在内核中使用以下行编写testing图像:

 write_imagef(dst, (int2)(x,y), (float4)((float)x / 512.0f, 0, 0, 0)); 

我得到以下图像:

红色通道测试梯度

第一个奇怪的是蓝色通道正在设置,而不是红色。 我不知道为什么,因为我总是加载和保存图像的RGBA顺序。 其次,条纹是非常不寻常的,我不知道如何解释这一点。

如果我在内核中使用以下行:

 write_imagef(dst, (int2)(x,y), (float4)(0, (float)y / 512.0f, 0, 0)); 

我得到以下图像:

在这里输入图像说明

这看起来是我期望的方式。

如果有必要,我可以提供更多的代码,但是在完全相同的线束中使用灰度内核是完美的。 另外一个没有在这里列出的内核只是复制所有的像素。

我在OpenCL 1.2上运行代码和Nvidia Geforce 980M

我还没有看到任何明显的东西。 一件奇怪的事情是:你的图像是CL_RGBA,CL_UNORM_INT8,但是你正在将它读入浮动数组中? 你怎么显示它呢? 其次,我不熟悉你的内核启动技术; 什么是filter ,是维度为2? 关于你看到的问题,我build议使用消除的过程来找出问题所在。 例如,(1)如果删除条件并复制所有像素,是否获得整个图像? (2)如果在条件为假的情况下写黑色,而不是在基于X位置的红色通道渐变和基于Y位置的绿色通道渐变写入黑色。 你得到一个双梯度? 根据结果​​,继续分解问题,直到find原因。 它看起来很像行间距问题,也许在显示function?

好的,所以问题是我读的高度和宽度是倒退的方式,即

 width = image.rows; height = image.cols; 

本来应该

 height = image.rows; width = image.cols; 

有了这个更正,其余的代码可以保持不变,除了我保存图像到磁盘的最后一行,这里的值需要再次交换,即

 cv::imwrite(filename_out, cv::Mat(width, height, CV_8UC4, oup)); 

需要改变为:

 cv::imwrite(filename_out, cv::Mat(height, width, CV_8UC4, oup)); 

我认为这最终归结为一个图像的matrix方法,其中第一个坐标实际上是行号,这是高度,第二个坐标是列号,这是宽度。

提到的诊断@Dithermaster确实有帮助,打印出假定的宽度和高度,这是最终不正确的。

有趣的是,通过在代码中的这两个错误的像素复制像素工作正常,但一旦你开始执行基于x,y坐标的行动,你会得到一些非常时髦的结果。