1

我正在尝试自学 C++AMP,并且想从我所在领域的一个非常简单的任务开始,那就是图像处理。我想将每像素 24 位的 RGB 图像(位图)转换为每像素 8 位的灰度图像。图像数据以unsigned char数组形式提供(从Bitmap::LockBits(...)等处获得)

我知道 C++AMP 出于某种原因无法通过or处理charorunsigned char数据,因此我尝试根据该博客使用 s 。这里解释了如何写入 8bpp 纹理,尽管 VisualStudio 2013 告诉我已弃用。arrayarray_viewtexturewriteonly_texture_view

我的代码抛出运行时异常,提示“无法调度内核”。异常的完整文本很长:

ID3D11DeviceContext::Dispatch:计算着色器单元插槽 0 中的无序访问视图 (UAV) 具有格式 (R8_UINT)。这种格式不支持像 UAV 一样从着色器中读取。如果着色器实际使用视图,则这种不匹配是无效的(例如,由于着色器代码分支,它没有被跳过)。不幸的是,不可能让所有硬件实现都支持将此格式作为 UAV 读取,尽管该格式可以作为 UAV 写入。如果着色器只需要对该资源执行读取而不是写入,请考虑使用着色器资源视图而不是 UAV。

到目前为止我使用的代码是这样的:

namespace gpu = concurrency;

gpu::extent<3> inputExtent(height, width, 3);
gpu::graphics::texture<unsigned int, 3> inputTexture(inputExtent, eight);
gpu::graphics::copy((void*)inputData24bpp, dataLength, inputTexture);
gpu::graphics::texture_view<unsigned int, 3> inputTexView(inputTexture);
gpu::graphics::texture<unsigned int, 2> outputTexture(width, height, eight);
gpu::graphics::writeonly_texture_view<unsigned int, 2> outputTexView(outputTexture);

gpu::parallel_for_each(outputTexture.extent,
    [inputTexView, outputTexView](gpu::index<2> pix) restrict(amp) {
    gpu::index<3> indR(pix[0], pix[1], 0);
    gpu::index<3> indG(pix[0], pix[1], 1);
    gpu::index<3> indB(pix[0], pix[1], 2);
    unsigned int sum = inputTexView[indR] + inputTexView[indG] + inputTexView[indB];
    outputTexView.set(pix, sum / 3);
});

gpu::graphics::copy(outputTexture, outputData8bpp);

这个异常的原因是什么,我可以做些什么来解决这个问题?

4

1 回答 1

1

我自己也一直在学习 C++Amp,遇到了与您的问题非常相似的问题,但就我而言,我需要处理 16 位图像。

很可能,这个问题可以使用纹理来解决,尽管由于缺乏经验我无法帮助你。

所以,我所做的基本上是基于位掩码。

首先,欺骗编译器以便让您编译:

unsigned int* sourceData = reinterpret_cast<unsigned int*>(source);
unsigned int* destData   = reinterpret_cast<unsigned int*>(dest);

接下来,您的数组查看器必须查看您的所有数据。请注意,您的数据实际上是 32 位大小的。因此,您必须进行转换(因为 16 位而分为 2,8 位使用 4)。

concurrency::array_view<const unsigned int> source( (size+ 7)/2, sourceData) );
concurrency::array_view<unsigned int> dest( (size+ 7)/2, sourceData) );

现在,您可以编写一个典型的 for_each 块了。

typedef concurrency::array_view<const unsigned int> OriginalImage;
typedef concurrency::array_view<unsigned int> ResultImage;

bool Filters::Filter_Invert()
{
    const int size = k_width*k_height;
    const int maxVal = GetMaxSize();

    OriginalImage& im_original = GetOriginal();
    ResultImage& im_result = GetResult();
    im_result.discard_data();

    parallel_for_each(
        concurrency::extent<2>(k_width, k_height), 
        [=](concurrency::index<2> idx) restrict(amp)
    {
        const int pos = GetPos(idx);
        const int val = read_int16(im_original, pos);

        write_int16(im_result, pos, maxVal - val);
    });

    return true;
}

int Filters::GetPos( const concurrency::index<2>& idx )  restrict(amp, cpu)
{
    return idx[0] * Filters::k_height + idx[1];
}

魔法来了:

template <typename T>
unsigned int read_int16(T& arr, int idx) restrict(amp, cpu)
{
    return (arr[idx >> 1] & (0xFFFF << ((idx & 0x7) << 4))) >> ((idx & 0x7) << 4);
}

template<typename T>
void write_int16(T& arr, int idx, unsigned int val) restrict(amp, cpu)
{
    atomic_fetch_xor(&arr[idx >> 1], arr[idx >> 1] & (0xFFFF << ((idx & 0x7) << 4)));
    atomic_fetch_xor(&arr[idx >> 1], (val & 0xFFFF) << ((idx & 0x7) << 4));
}

请注意,此方法适用于 8 位的 16 位,但将其调整为 8 位应该不会太难。事实上,这是基于 8 位版本,不幸的是,我找不到参考。

希望能帮助到你。

大卫

于 2014-03-30T00:11:03.643 回答