2012-10-15 3 views
5

Nvidia Performance Primitives (NPP) fornisce la funzione nppiFilter per convalidare un'immagine fornita dall'utente con un kernel fornito dall'utente. Per i kernel di convoluzione 1D, nppiFilter funziona correttamente. Tuttavia, nppiFilter produce un'immagine garbage per i kernel 2D.NPidia NPP nppiFilter produce dati inutili durante la convalida con il kernel 2d

ho usato l'immagine tipica Lena come input: enter image description here


Ecco il mio esperimento con un nucleo di convoluzione 1D, che produce buona uscita.

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

output del codice precedente con kernel [-1 0 1] - appare un'immagine gradiente ragionevole come: enter image description here


Tuttavia, nppiFilter emette un'immagine garbage se uso un kernel 2D convoluzione . Qui sono le cose che ho cambiato dal codice di cui sopra per eseguire con il kernel 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); 

Di seguito si riporta l'immagine di uscita utilizzando il kernel 2D [-1 0 1; -1 0 1; -1 0 1].

Cosa sto sbagliando?

enter image description here

This StackOverflow post descrive un problema simile, come mostrato nell'immagine utente di Steenstrup in: http://1ordrup.dk/kasper/image/Lena_boxFilter5.jpg


Alcune note finali:

  • con il kernel 2D, per alcuni ancora valori (ad es. NppiPoint oAnchor = {0, 0} o {1, 1}), ricevo l'errore -24, che t si adatta a NPP_TEXTURE_BIND_ERROR in base allo NPP User Guide. Questo problema è stato brevemente menzionato in this StackOverflow post.
  • Questo codice è molto dettagliato. Questa non è la domanda principale, ma qualcuno ha qualche suggerimento su come rendere questo codice più conciso?

risposta

2

Si sta utilizzando un allocatore di memoria 2D per l'array del kernel. Gli array di kernel sono array 1D densi, non array a 2D come la tipica immagine NPP.

Sostituire semplicemente il malloc CUDA 2D con un semplice cuda malloc di dimensioni kernelWidth * kernelHeight * sizeof (Npp32s) e eseguire una normale memo di CUDA non 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); 

Per inciso, un "fattore di scala" di 1 non si traduce in alcun ridimensionamento. Il ridimensionamento avviene con i fattori 2^(- Fattore di scala).

+0

Ah, fantastico. Sto provando 1D 'cudaMalloc' e 1D' cudaMemcpy' ora. Inoltre, sembra che 'ScaleFactor = 0' non dia ridimensionamento, corretto? – solvingPuzzles

+0

Facendo un malloc 1D e memcpy risolto il problema !! Grazie! Ecco l'immagine elaborata con il kernel 2d 3x3: http://i.stack.imgur.com/wziix.png – solvingPuzzles

+1

Se NPP scala di '2^(- Fattore di scala)', allora penso che 'ScaleFactor = 0' dovrebbe dare un divisore di 1. Tuttavia, l'impostazione 'ScaleFactor = 0' mi dà un'immagine vuota. – solvingPuzzles