【发布时间】:2014-01-27 12:10:17
【问题描述】:
我在理解 cmets 中提到的批量加载时遇到了一些困难。为了计算像素中的卷积,大小为 5 的掩码必须以该特定像素为中心。图像被分成瓷砖。应用卷积掩码后的这些图块是最终输出图块,其大小为TILE_WIDTH*TILE_WIDTH。对于属于输出图块边界的像素,当该图块属于图像的边界时,掩码必须从相邻图块借用一些像素。否则,这些借用的值被分配为零。这两个步骤在
if (srcY >= 0 && srcY < height && srcX >= 0 && srcX < width)
N_ds[destY][destX] = I[src];
else
N_ds[destY][destX] = 0;
因此,共享内存数组的每一侧都有TILE_WIDTH + Mask_width - 1 维度。我不清楚代码的以下部分。
-
destY和destX索引。 将输出索引除以输入平铺宽度是什么意思? -
srcY添加srcX索引。 为什么destY和destX索引参与srcY添加srcX索引?srcY = blockIdx.y * TILE_WIDTH + destY - Mask_radius;srcX = blockIdx.x * TILE_WIDTH + destX - Mask_radius; - 为什么在第二次加载中我们使用偏移量
TILE_WIDTH * TILE_WIDTH? - 一般来说,有两个加载的直观解释是什么?
- 能否在所有这些问题之后根据下图给出一个直观的示例?
- 谢谢!
编辑:图片已添加。绿色是输出图块,红色是掩码以 114 索引为中心。很明显,面具借用了不同瓷砖的元素。 最后,这张图片指向一个频道。
示例:根据下图,我尝试编写了一个示例。基于destY=0 和destX=0,输出图块具有blockIdx.x=1 和blockIdx.y=1。还,
srcY = 1*6+0-3=3、srcX = 3 和 src = (3*18+3)*3+0=171。根据计算和图像示例,我们没有匹配项。在第一个共享内存位置中,应该存储的值是具有全局索引57 的值。上述计算有什么问题?有人可以帮忙吗?
#define Mask_width 5
#define Mask_radius Mask_width/2
#define TILE_WIDTH 16
#define w (TILE_WIDTH + Mask_width - 1)
#define clamp(x) (min(max((x), 0.0), 1.0))
__global__ void convolution(float *I, const float* __restrict__ M, float *P,
int channels, int width, int height) {
__shared__ float N_ds[w][w];
int k;
for (k = 0; k < channels; k++) {
// First batch loading
int dest = threadIdx.y * TILE_WIDTH + threadIdx.x,
destY = dest / w, destX = dest % w,
srcY = blockIdx.y * TILE_WIDTH + destY - Mask_radius,
srcX = blockIdx.x * TILE_WIDTH + destX - Mask_radius,
src = (srcY * width + srcX) * channels + k;
if (srcY >= 0 && srcY < height && srcX >= 0 && srcX < width)
N_ds[destY][destX] = I[src];
else
N_ds[destY][destX] = 0;
// Second batch loading
dest = threadIdx.y * TILE_WIDTH + threadIdx.x + TILE_WIDTH * TILE_WIDTH;
destY = dest / w, destX = dest % w;
srcY = blockIdx.y * TILE_WIDTH + destY - Mask_radius;
srcX = blockIdx.x * TILE_WIDTH + destX - Mask_radius;
src = (srcY * width + srcX) * channels + k;
if (destY < w) {
if (srcY >= 0 && srcY < height && srcX >= 0 && srcX < width)
N_ds[destY][destX] = I[src];
else
N_ds[destY][destX] = 0;
}
__syncthreads();
float accum = 0;
int y, x;
for (y = 0; y < Mask_width; y++)
for (x = 0; x < Mask_width; x++)
accum += N_ds[threadIdx.y + y][threadIdx.x + x] * M[y * Mask_width + x];
y = blockIdx.y * TILE_WIDTH + threadIdx.y;
x = blockIdx.x * TILE_WIDTH + threadIdx.x;
if (y < height && x < width)
P[(y * width + x) * channels + k] = clamp(accum);
__syncthreads();
}
}
【问题讨论】:
-
好的,我认为它变得更加清晰了。我阅读了为了从 1D 索引到 2d,您执行以下操作:Y = index1D / 宽度,X = index1D % 宽度。我不知道。
-
但它不除以
TILE_WIDTH?相反,除以输入瓦片宽度,w。为什么会这样? -
...因为索引在元素中,而不是在图块中。
-
我希望我能理解你。你是什么意思“在元素中”?通过将宽度为
TILE_WIDTH的索引方案与另一个平铺宽度分开,我们有什么转换?你能不能更直观一点。在过去的两天里,我试图在脑海中弄清楚这段代码,但缺少重要的知识或理解深度。谢谢。 -
使用共享内存适应3D卷积:[这里][1] [1]:stackoverflow.com/questions/22577857/…