繁体   English   中英

Nvidia NPP nppiFilter在与2d内核进行卷积时产生垃圾

[英]Nvidia NPP nppiFilter produces garbage when convolving with 2d kernel

Nvidia Performance Primitives(NPP)提供nppiFilter函数,用于将用户提供的图像与用户提供的内核进行卷积。 对于1D卷积内核, 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] [-1 0 1; -1 0 1; -1 0 1] [-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] [-1 0 1; -1 0 1; -1 0 1] [-1 0 1; -1 0 1; -1 0 1]

我究竟做错了什么?

在此输入图像描述

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


最后几点说明:

  • 对于2D内核,对于某些锚值(例如NppiPoint oAnchor = {0, 0}{1, 1} ),我得到错误-24 ,根据NPP用户指南转换为NPP_TEXTURE_BIND_ERROR StackOverflow帖子中简要提到了此问题。
  • 这段代码非常详细。 这不是主要问题,但有没有人对如何使这些代码更简洁有任何建议?

您正在为内核阵列使用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)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM