6

Nvidia Performance Primitives(NPP)は、nppiFilterユーザー提供のイメージをユーザー提供のカーネルで畳み込むための機能を提供します。1D畳み込みカーネルの場合、nppiFilter正しく機能します。ただし、nppiFilter2Dカーネルのガベージイメージを生成しています。

私は入力として典型的なレナ画像を使用しました: ここに画像の説明を入力してください


これが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]です。

私は何が間違っているのですか?

ここに画像の説明を入力してください

このStackOverflowの投稿では、ユーザーSteenstrupの画像に示されているように、同様の問題について説明しています。http: //1ordrup.dk/kasper/image/Lena_boxFilter5.jpg


最後の注意事項:

  • 2Dカーネルでは、特定のアンカー値(NppiPoint oAnchor = {0, 0}または{1, 1})に対してエラーが発生します。これは、 NPPユーザーガイドに従って-24解釈されます。この問題は、このStackOverflowの投稿で簡単に説明されています。NPP_TEXTURE_BIND_ERROR
  • このコードは非常に冗長です。これは主な質問ではありませんが、このコードをより簡潔にする方法について誰かが何か提案がありますか?
4

1 に答える 1

3

カーネルアレイに2Dメモリアロケータを使用しています。カーネルアレイは高密度の1Dアレイであり、一般的なNPPイメージのように2Dストライドアレイではありません。

2DCUDAmallocをkernelWidth*kernelHeight * sizeof(Npp32s)のサイズの単純なcuda mallocに置き換えるだけで、2Dのmemcopyではなく通常のCUDAmemcopyを実行できます。

//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)で発生します。

于 2012-10-15T23:15:02.457 に答える