网格维度和块维度的混淆

6
我想解决 Udacity 课程第一课结尾的问题,但我不确定是我打错了还是代码本身就有误。
void your_rgba_to_greyscale(const uchar4 * const h_rgbaImage, uchar4 * const d_rgbaImage, unsigned char* const d_greyImage, size_t numRows, size_t numCols)
{
    size_t totalPixels = numRows * numCols;
    size_t gridRows = totalPixels / 32;
    size_t gridCols = totalPixels / 32;
    const dim3 blockSize(32,32,1);
    const dim3 gridSize(gridCols,gridRows,1);
    rgba_to_greyscale<<<gridSize, blockSize>>>(d_rgbaImage, d_greyImage, numRows, numCols);
    cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError());
}

另一种方法是:
void rgba_to_greyscale(const uchar4* const rgbaImage, unsigned char* const greyImage, int numRows, int numCols)
{   
    int x = (blockIdx.x * blockDim.x) + threadIdx.x;
    int y = (blockIdx.y * blockDim.y) + threadIdx.y;
    uchar4 rgba = rgbaImage[x * numCols + y];
    float channelSum = 0.299f * rgba.x + 0.587f * rgba.y + 0.114f * rgba.z;
    greyImage[x * numCols + y] = channelSum;
}

错误消息如下所示:
libdc1394 error: failed to initialize libdc1394
Cuda error at student_func.cu:76
unspecified launch failure cudaGetLastError()
we were unable to execute your code. Did you set the grid and/or block size correctly?

但随后它表示代码已经编译完成,
Your code compiled!
error output: libdc1394 error: Failed to initialize libdc1394
Cuda error at student_func.cu:76
unspecified launch failure cudaGetLastError()

第一段代码块的最后一行是第76行,据我所知,我没有对其进行任何更改。 第76行如下:

rgba_to_greyscale<<<gridSize, blockSize>>>(d_rgbaImage, d_greyImage, numRows, numCols);

我实际上找不到 cudaGetLastError() 的声明。

我主要关心如何设置网格/块的维度以及第一种方法是否正确地映射了一维像素位置数组和我的线程之间的关系。

编辑: 我想我误解了一些东西。 numRows 是垂直方向上的像素数吗?numCols 是水平方向上的像素数吗?

我的块由 8 x 8 个线程组成,其中每个线程代表一个像素?如果是这样,我假设这就是我在计算 gridRows 时必须除以 4 的原因,因为图像不是正方形?我假设我也可以创建一个比例为 2:1 的列:行的块?

Screen shot

编辑 2: 我刚试图将我的块更改为2:1的比例,以便我可以将numRowsnumCol除以相同的数字,但现在它显示底部和侧面有空白区域。为什么底部和侧面都有空白区域?我没有改变我的网格或块的y维度。

enter image description here


内核中还有一个错误,请参见答案。 此外,您不需要除以不同的数字,否则会重复覆盖某些像素或错过一些像素。 - ShPavel
1个回答

10

每个块处理32 * 32像素,总共有(totalPixels / 32) * (totalPixels / 32)个块,因此你要处理totalPixels ^ 2个像素——这似乎是错误的。

第一个是错误的,这应该是正确的:

const dim3 blockSize(32,32,1);

size_t gridCols = (numCols + blockSize.x - 1) / blockSize.x;
size_t gridRows = (numRows + blockSize.y - 1) / blockSize.y;

这是2D图像处理中的一种常见模式,你可以记住它。

在示例图像中,如果图像大小不是2的幂,并且您希望块覆盖整个图像(甚至更多),则必须满足以下条件:

格子列数(gridCols)* 块大小.x(blockSize.x)≥ 列数(numCols)
格子行数(gridRows)* 块大小.y(blockSize.y)≥ 行数(numRows)

您选择块大小,并基于它来计算需要覆盖所有图像所需的块数。

然后,在内核中,您必须检查您是否未超出图像范围,以处理大小不恰当的情况。

另一个问题出现在内核中,必须是(y * numCols + x),而不是相反。

内核:

int x = (blockIdx.x * blockDim.x) + threadIdx.x;
int y = (blockIdx.y * blockDim.y) + threadIdx.y;

if(x < numCols && y < numRows)
{
    uchar4 rgba = rgbaImage[y * numCols + x];
    float channelSum = 0.299f * rgba.x + 0.587f * rgba.y + 0.114f * rgba.z;
    greyImage[y * numCols + x] = channelSum;
}

国际电话区号:

const dim3 blockSize(4,32,1); // may be any

size_t gridCols = (numCols + blockSize.x - 1) / blockSize.x;
size_t gridRows = (numRows + blockSize.y - 1) / blockSize.y;

const dim3 gridSize(gridCols,gridRows,1);
rgba_to_greyscale<<<gridSize, blockSize>>>(d_rgbaImage, d_greyImage, numRows, numCols);
cudaDeviceSynchronize(); 
checkCudaErrors(cudaGetLastError());

该死,我感觉自己在做的事情越来越难以理解(

)


是的,你说得对,我的gridrows和gridcols是错误的,谢谢指出。为什么要在gridRows和gridCols上加1? - Hans Rudel
好的,我只是幸运地让这些参数适合。我现在明白了,分配比像素更多的线程然后确保当前线程在像素范围内非常整洁。如果图片不是32个块的精确匹配,我曾经想过该怎么办。最后一个问题,声明块和网格的dim3时,它总是x,y,z,其中x =水平等吗?再次感谢您的帮助+1 :) - Hans Rudel
1
它总是有3个坐标,但它并没有真正的绑定到'水平'或'垂直'。 它只是更容易想象它们像通常的x y z维度,但没有什么可以阻止您使用'y'或'z'坐标来索引行或列或其他内容。 - ShPavel
好的,我只是在确认一下。再次感谢 :) - Hans Rudel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接