对于到达这里的人,在试图弄清楚如何使用OpenCL进行高斯模糊或灰度时,最终的工作代码为here。请注意,在那个repo中,我实际上是使用Nvidia的Docker包装器在Docker中运行整个GPU访问。您可以在“Dockerfile”中查看为使代码运行而需要采取的步骤,或者如果您有这样的设置并且在Nvidia GPU上运行,则只需使用Nvidia-Docker运行它。
在OpenCL图像过滤器应用程序中使用以下内核,我得到了预期的结果,即输入图像的返回灰度版本:
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));
}
到目前为止,这么好。然后我尝试创建一个只复制图像顶部和左边框的内核:
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));
}
}
我正在以这种方式加载输入图像:
// 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));
为什么图像的处理方式如此?只选择y坐标小于10的像素似乎有效,但选择x坐标小于10的像素似乎在图像上错开。
如果我在内核中使用以下行编写测试图像:
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
答案 0 :(得分:2)
我还没有看到任何明显的东西。一个奇怪的事情:你的图像是CL_RGBA,CL_UNORM_INT8但是你正在把它读成一个浮点数组?你是如何展示它的?其次,我不熟悉你的内核启动技术;什么是filter
,是否以2的维度启动?关于你所看到的问题,我建议使用消除过程来找出问题所在。例如,(1)如果删除条件并复制所有像素,您会获得整个图像吗? (2)如果你根据X位置和基于Y位置的绿色通道渐变编写红色通道渐变,那么如何在条件为假的情况下写入黑色。你有双梯度吗?根据结果,继续分解问题,直到找到原因。它看起来很像行间距问题,也许在显示功能中?
答案 1 :(得分:0)
好的,问题是我读高度和宽度的方式是向后的,即
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));
我认为这最终归结为图像的矩阵方法,其中第一个坐标实际上是行号,即高度,第二个坐标是列号,即宽度。
@Dithermaster提到的诊断确实有帮助,打印出假定的宽度和高度也是如此,这最终是错误的。
有趣的是,通过在代码中同时存在这些错误,像素复制的像素工作正常,但是一旦开始基于x,y坐标执行操作,就会得到一些非常时髦的结果。