2012-10-15 1 views
5

Nvidia Performance Primitives (NPP) bietet die nppiFilter Funktion zum Falten eines vom Benutzer bereitgestellten Bildes mit einem vom Benutzer bereitgestellten Kernel. Für 1D-Faltungskerne funktioniert nppiFilter ordnungsgemäß. nppiFilter erzeugt jedoch ein Müllbild für 2D-Kernel.Nvidia NPP nppiFilter produziert Müll beim Falten mit 2d Kernel

verwenden ich die typische Lena Bild als Eingabe: enter image description here


mit meinem Experiment Hier 1D Faltungskernen, die ein gute Ausgangssignal erzeugt.

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

Ausgabe des obigen Codes mit Kernel [-1 0 1] - es sieht aus wie eine vernünftige Gradientenbild: enter image description here


jedoch nppiFilter gibt einen Müll Bild, wenn ich ein 2D Faltungskerne verwenden . Hier sind die Dinge, die ich von dem obigen Code geändert mit dem 2D-Kernel laufen [-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); 

Unter dem Ausgabebild ist die Kernel-2D mit [-1 0 1; -1 0 1; -1 0 1].

Was mache ich falsch?

enter image description here

This StackOverflow post beschreibt ein ähnliches Problem, wie in Benutzer Steenstrup das Bild gezeigt: http://1ordrup.dk/kasper/image/Lena_boxFilter5.jpg


Einige abschließende Hinweise:

  • Mit dem 2D-Kernel für bestimmte Anker Werte (zB NppiPoint oAnchor = {0, 0} oder {1, 1}), bekomme ich Fehler -24, die t Überträgt NPP_TEXTURE_BIND_ERROR gemäß der NPP User Guide. Dieses Problem wurde kurz in this StackOverflow post erwähnt.
  • Dieser Code ist sehr ausführlich. Dies ist nicht die Hauptfrage, aber hat jemand irgendwelche Vorschläge, wie man diesen Code prägnanter gestalten kann?

Antwort

2

Sie verwenden einen 2D-Speicherzuordner für das Kernel-Array. Kernel-Arrays sind dichte 1D-Arrays, keine 2D-strided-Arrays wie das typische NPP-Image.

Ersetzen Sie einfach die 2D CUDA malloc mit einem einfachen Cuda malloc der Größe kernelWidth * kernelHeight * sizeof (Npp32s) und machen Sie eine normale CUDA memcopy nicht 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); 

Nebenbei, ein "Maßstabsfaktor" von 1 übersetzt nicht zu keiner Skalierung. Die Skalierung erfolgt mit den Faktoren 2^(- ScaleFactor).

+0

Ah, großartig. Ich probiere die 1D 'cudaMalloc' und 1D' cudaMemcpy' jetzt aus. Es klingt auch wie 'ScaleFactor = 0' würde keine Skalierung geben, richtig? – solvingPuzzles

+0

Doing ein 1D malloc und memcpy behoben das Problem !! Vielen Dank! Hier ist das Bild, das mit dem 2d 3x3 Kernel bearbeitet wurde: http://i.stack.imgur.com/wziix.png – solvingPuzzles

+1

Wenn NPP um '2^(- ScaleFactor)' skaliert, dann sollte 'ScaleFactor = 0' ein a geben Divisor von 1. Jedoch, Einstellung von "ScaleFactor = 0" gibt mir ein leeres Bild. – solvingPuzzles