2012-10-15 23 views
5

Nvidia Performance Primitives (NPP)は、ユーザーが提供するイメージをユーザー提供のカーネルで畳み込む機能を提供します。 1D畳み込みカーネルの場合は、nppiFilterが適切に動作します。しかし、nppiFilterは、2Dカーネルのガベージイメージを生成しています。Nvidia NPP nppiFilterは、2dカーネルと畳み込み時にガベージを生成します。

私は、入力として、典型的なレナ・イメージを使用: enter image description here


ここで良好な出力を生成し1D畳み込みカーネル、と私の実験です。カーネル[-1 0 1]と上記コードの

#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); 
} 

出力 - それは合理的な勾配画像のように見える:私は2Dコンボリューション・カーネルを使用する場合 enter image description here


しかし、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]を使用して、出力画像です。

私は間違っていますか?

enter image description here

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で簡単に説明しました。
  • このコードは非常に冗長です。これは主な質問ではありませんが、誰かがこのコードをより簡潔にするための提案はありますか?

答えて

2

カーネルアレイに2Dメモリアロケータを使用しています。カーネル配列は、典型的なNPP画像と同じように、2Dストライド配列ではなく密な1D配列です。

2D CUDA mallocをkernelWidth * kernelHeight * sizeof(Npp32s)のサイズの単純なmudocで置き換え、通常の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)の係数で行われます。

+0

ああ、素晴らしいです。私は今、1Dの 'cudaMalloc'と1D' 'cudaMemcpy'を試しています。また、 'ScaleFactor = 0'のように、スケーリングを与えないように聞こえるでしょうか? – solvingPuzzles

+0

1Dのmallocとmemcpyを使って問題を解決しました!ありがとう! 2次元3x3カーネルで処理された画像は次のとおりです:http://i.stack.imgur.com/wziix.png – solvingPuzzles

+1

NPPが '2 ^( - ScaleFactor)'でスケールすると、 'ScaleFactor = 0'はしかし、ScaleFactor = 0を設定すると空白の画像が表示されます。 – solvingPuzzles

関連する問題