【问题标题】:2D Image Indexing Bug in CUDA KernelCUDA 内核中的 2D 图像索引错误
【发布时间】:2012-01-08 16:53:44
【问题描述】:

我正在使用 CUDA 对图像进行线性过滤。我使用 2D 线程块和 2D 网格来使问题自然。以下是我的索引方式:(heightwidth 是图像尺寸)

dim3 BlockDim(16,16);

dim3 GridDim;
GridDim.x = (width + 15) / 16;
GridDim.y = (height + 15) / 16;

在内核中,我访问的位置如下:

unsigned int xIndex = blockIdx.x*16+ threadIdx.x;
unsigned int yIndex = blockIdx.y*16+ threadIdx.y;
unsigned int tid = yIndex * width + xIndex;

我想返回四个边界(稍后我会满足它们)。我这样做是:

if(yIndex>=height-N || xIndex>=width-N || yIndex<N || xIndex<N)
  return;

其中 N 是我不想计算的每个边界处的像素数。

问题:

代码在所有标准图像尺寸上都能正常运行。但是对于某些随机图像尺寸,它会显示对角线。例如,在我的情况下,500x333 图像(即使没有维度是 16 的倍数)显示正确的输出,而 450x365 显示输出中的对角线。即使我只返回网格的额外线程而没有其他类似的东西,问题仍然存在:

if(yIndex>=height || xIndex>=width)
return;

代码保持不变,一些输入运行良好,而另一些则不然。任何人都可以发现错误吗?我已在此处附上输入和输出示例:IMAGES 谢谢!

更新:

内核代码(简化为返回输入图像,但同样的问题)

__global__ void filter_8u_c1_kernel(unsigned char* in, unsigned char* out, int width, int height, float* filter, int fSize)
{
    unsigned int xIndex = blockIdx.x*BLOCK_SIZE + threadIdx.x;
    unsigned int yIndex = blockIdx.y*BLOCK_SIZE + threadIdx.y;
    unsigned int tid = yIndex * width + xIndex;

    unsigned int N = filterSize/2;

    if(yIndex>=height-N || xIndex>=width-N || yIndex<N || xIndex<N)
        return;

       /*Filter code removed, still gives the same problem*/

    out[tid] = in[tid];
}

更新 2:

我还通过反转 if 条件删除了 return 语句。但问题依然存在。

if(yIndex<=height-N && xIndex<=width-N && yIndex>N && xIndex>N){

  /*Kernel Code*/

}

【问题讨论】:

  • 如果没有看到代码,将很难回答,但我会非常怀疑ever 在内核中使用 return 语句。如果代码中存在内存或指令同步障碍,那么很可能是返回语句本身导致了问题。
  • 我已经在更新中添加了代码。和我之前说的差不多。
  • 您能否将您正在使用的内核参数和启动参数添加到失败的案例中? filterSize 目前也未定义,应该是 fSize
  • 我已经在问题中提到了启动参数。 filterSize 可以是任何值,例如 3,5,7,9 .. 它有助于计算 N,它设置边界处的偏移像素。实际上,如果我将 N 硬编码为任何值,它没有任何区别。在这种配置下,我只复制输入图像以输出所有标准尺寸的作品,还有一些其他的,但图像尺寸很少,例如 450x364,它会给出错误的输出。请看附件。

标签: cuda


【解决方案1】:

您仍然没有很好地描述很多事情,但是根据您发布的信息,我构建了一个我猜想是一个合理的重现案例,其参数与您说它失败的案例相匹配(450 x 364 与filterSize=5):

#include <stdio.h>
#include <assert.h>

template<int filterSize>
__global__ void filter_8u_c1_kernel(unsigned char* in, unsigned char* out, int width, int height, float* filter, int fSize)
{
    unsigned int xIndex = blockIdx.x*blockDim.x + threadIdx.x;
    unsigned int yIndex = blockIdx.y*blockDim.y + threadIdx.y;
    unsigned int tid = yIndex * width + xIndex;

    unsigned int N = filterSize/2;

    if(yIndex>=height-N || xIndex>=width-N || yIndex<N || xIndex<N)
        return;

    out[tid] = in[tid];
}

int main(void)
{
    const int width = 450, height = 365, filterSize=5;
    const size_t isize = sizeof(unsigned char) * size_t(width * height);
    unsigned char * _in, * _out, * out;

    assert( cudaMalloc((void **)&_in, isize) == cudaSuccess ); 
    assert( cudaMalloc((void **)&_out, isize) == cudaSuccess ); 
    assert( cudaMemset(_in, 'Z', isize) == cudaSuccess );
    assert( cudaMemset(_out, 'A', isize) == cudaSuccess );

    const dim3 BlockDim(16,16);
    dim3 GridDim;
    GridDim.x = (width + BlockDim.x - 1) / BlockDim.x;
    GridDim.y = (height + BlockDim.y - 1) / BlockDim.y;

    filter_8u_c1_kernel<filterSize><<<GridDim,BlockDim>>>(_in,_out,width,height,0,0);
    assert( cudaPeekAtLastError() == cudaSuccess );

    out = (unsigned char *)malloc(isize);
    assert( cudaMemcpy(out, _out, isize, cudaMemcpyDeviceToHost) == cudaSuccess);

    for(int i=0; i<width; i++) {
        fprintf(stdout, "%d: ", i);
        for(int j=0; j<height; j++) {
            unsigned int idx = i + j*width;
            fprintf(stdout, "%c", out[idx]);
        }
        fprintf(stdout, "\n");
    }

    return cudaThreadExit();
}

运行时它完全符合我的预期,用输入覆盖输出内存,除了第一行和最后两行以及其间所有行中的第一个和最后两个条目。这是在具有计算 1.2 GPU 的 OS X 10.6.5 上使用 CUDA 3.2 运行的。因此,无论您的代码中发生了什么,在我的重现案例中都没有发生,这要么意味着我误解了您所写的内容,要么是您未描述的其他内容导致了问题。

【讨论】:

  • 我在我的机器上测试了你的代码,它运行良好,输出正确。所以这让我从整个框架中提取了我的代码,并专门为这个内核制作了一个项目。我正在使用 OpenCV(免费提供)阅读图像。当输入通过 OpenCV 读取的图像时,相同的内核在 450x364 上给出不正确的输出。标准图像尺寸工作正常,您认为这可能是 OpenCV 问题吗?我在这里附上完整的项目,请看:hardwareinsight.com/so.zip
  • @JawadMasood:OpenCV 图像并不总是连续的,即所有字节在内存中都是连续的。图像行之间可能存在间隙。您如何访问图像数据?如果bool Mat::isContinuous() 返回false,您应该在考虑size_t Mat::step 的情况下访问它。见OpenCV Mat documentationthis question
  • @JawadMasood:抱歉,没有。您问了一个关于内核索引方案的问题,我相信它已经得到了令人满意的回答。我不会钻研一个庞大的 OpenCV 项目来尝试找到你的错误。如果您可以隔离一个与 OpenCV 相关的问题,那么我建议您发布一个新的、以 OpenCV 为重点的问题。
  • 其实我发现了问题。 imageStep 并不总是等于 width*numberOfChannel。在这种情况下,450x364 的图像的步长为 452!!! OpenCV 还读取步长。我所要做的就是改变宽度以进入调用者函数。您在解决这个问题方面提供了很多帮助。我接受你的答案是正确的!谢谢:)
猜你喜欢
  • 2013-02-01
  • 2013-01-15
  • 2019-09-24
  • 1970-01-01
  • 2021-08-12
  • 2021-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多