【问题标题】:Nvidia NPP nppiFilter produces garbage when convolving with 2d kernelNvidia NPP nppiFilter 在与 2d 内核卷积时产生垃圾
【发布时间】:2012-10-05 20:15:17
【问题描述】:

Nvidia Performance Primitives (NPP) 提供nppiFilter 函数,用于将用户提供的图像与用户提供的内核进行卷积。对于一维卷积核,nppiFilter 可以正常工作。但是,nppiFilter 正在为 2D 内核生成垃圾图像。

我使用典型的 Lena 图像作为输入:


这是我对 1D 卷积核的实验,它产生了良好的输出。

#include <npp.h> // provided in CUDA SDK
#include <ImagesCPU.h> // these image libraries are also in CUDA SDK
#include <ImagesNPP.h>
#include <ImageIO.h>

void test_nppiFilter()
{
    npp::ImageCPU_8u_C1 oHostSrc;
    npp::loadImage("Lena.pgm", oHostSrc);
    npp::ImageNPP_8u_C1 oDeviceSrc(oHostSrc); // malloc and memcpy to GPU 
    NppiSize kernelSize = {3, 1}; // dimensions of convolution kernel (filter)
    NppiSize oSizeROI = {oHostSrc.width() - kernelSize.width + 1, oHostSrc.height() - kernelSize.height + 1};
    npp::ImageNPP_8u_C1 oDeviceDst(oSizeROI.width, oSizeROI.height); // allocate device image of appropriately reduced size
    npp::ImageCPU_8u_C1 oHostDst(oDeviceDst.size());
    NppiPoint oAnchor = {2, 1}; // found that oAnchor = {2,1} or {3,1} works for kernel [-1 0 1] 
    NppStatus eStatusNPP;

    Npp32s hostKernel[3] = {-1, 0, 1}; // convolving with this should do edge detection
    Npp32s* deviceKernel;
    size_t deviceKernelPitch;
    cudaMallocPitch((void**)&deviceKernel, &deviceKernelPitch, kernelSize.width*sizeof(Npp32s), kernelSize.height*sizeof(Npp32s));
    cudaMemcpy2D(deviceKernel, deviceKernelPitch, hostKernel,
                     sizeof(Npp32s)*kernelSize.width, // sPitch
                     sizeof(Npp32s)*kernelSize.width, // width
                     kernelSize.height, // height
                     cudaMemcpyHostToDevice);
    Npp32s divisor = 1; // no scaling

    eStatusNPP = nppiFilter_8u_C1R(oDeviceSrc.data(), oDeviceSrc.pitch(),
                                          oDeviceDst.data(), oDeviceDst.pitch(),
                                          oSizeROI, deviceKernel, kernelSize, oAnchor, divisor);

    cout << "NppiFilter error status " << eStatusNPP << endl; // prints 0 (no errors)
    oDeviceDst.copyTo(oHostDst.data(), oHostDst.pitch()); // memcpy to host
    saveImage("Lena_filter_1d.pgm", oHostDst); 
}

上面代码的输出,内核为[-1 0 1]——它看起来像一个合理的渐变图像:


但是,如果我使用 2D 卷积核,nppiFilter 会输出垃圾图像。以下是我从上面的代码更改为使用 2D 内核 [-1 0 1; -1 0 1; -1 0 1] 运行的内容:

NppiSize kernelSize = {3, 3};
Npp32s hostKernel[9] = {-1, 0, 1, -1, 0, 1, -1, 0, 1};
NppiPoint oAnchor = {2, 2}; // note: using anchor {1,1} or {0,0} causes error -24 (NPP_TEXTURE_BIND_ERROR)
saveImage("Lena_filter_2d.pgm", oHostDst);

下面是使用2D内核[-1 0 1; -1 0 1; -1 0 1]的输出图像。

我做错了什么?

This StackOverflow post 描述了一个类似的问题,如用户 Steenstrup 的图片所示:http://1ordrup.dk/kasper/image/Lena_boxFilter5.jpg


最后几点说明:

  • 使用 2D 内核,对于某些锚值(例如 NppiPoint oAnchor = {0, 0}{1, 1}),我收到错误 -24,根据 NPP User Guide 转换为 NPP_TEXTURE_BIND_ERROR。这个问题在this StackOverflow post 中有简要提及。
  • 此代码非常冗长。这不是主要问题,但有人对如何使这段代码更简洁有任何建议吗?

【问题讨论】:

    标签: c++ image-processing cuda convolution npp


    【解决方案1】:

    您正在为内核数组使用 2D 内存分配器。内核数组是密集的一维数组,而不是像典型的 NPP 图像那样的二维跨步数组。

    只需用大小为 kernelWidth*kernelHeight*sizeof(Npp32s) 的简单 cuda malloc 替换 2D CUDA malloc,然后执行普通的 CUDA memcopy 而不是 memcopy 2D。

    //1D instead of 2D
    cudaMalloc((void**)&deviceKernel, kernelSize.width * kernelSize.height * sizeof(Npp32s));
    cudaMemcpy(deviceKernel, hostKernel, kernelSize.width * kernelSize.height * sizeof(Npp32s), cudaMemcpyHostToDevice);
    

    顺便说一句,“比例因子”为 1 并不意味着没有比例。使用因子 2^(-ScaleFactor) 进行缩放。

    【讨论】:

    • 啊,太好了。我现在正在尝试一维 cudaMalloc 和一维 cudaMemcpy。另外,听起来ScaleFactor=0 不会进行缩放,对吗?
    • 执行一维 malloc 和 memcpy 解决了问题!!谢谢!这是使用 2d 3x3 内核处理的图像:i.stack.imgur.com/wziix.png
    • 如果 NPP 按2^(-ScaleFactor) 缩放,那么我认为ScaleFactor=0 的除数应该是1。但是,设置ScaleFactor=0 会给我一个空白图像。
    猜你喜欢
    • 1970-01-01
    • 2017-07-25
    • 2020-10-10
    • 2013-11-02
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 2012-04-26
    • 1970-01-01
    相关资源
    最近更新 更多