АЭС Nvidia nppiFilter выдает мусор при свертке с 2d ядром

Nvidia Performance Primitives (АЭС) обеспечивает nppiFilter функция для свертки предоставленного пользователем изображения с предоставленным пользователем ядром. Для 1D сверточных ядер, nppiFilter работает правильно. Тем не мение, nppiFilter создает изображение мусора для 2D-ядер.

Я использовал типичное изображение Лены в качестве входных данных:
введите описание изображения здесь


Вот мой эксперимент с 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] — это выглядит как разумное градиентное изображение:
введите описание изображения здесь


Тем не мение, nppiFilter выводит изображение мусора, если я использую 2D сверточное ядро. Вот то, что я изменил из приведенного выше кода для запуска с 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


Несколько заключительных замечаний:

  • С двумерным ядром для определенных значений привязки (например, NppiPoint oAnchor = {0, 0} или же {1, 1}) Я получаю ошибку -24, что переводится как NPP_TEXTURE_BIND_ERROR в соответствии с Руководство пользователя АЭС. Эта проблема была кратко упомянута в это сообщение StackOverflow.
  • Этот код очень многословен. Это не главный вопрос, но есть ли у кого-нибудь предложения, как сделать этот код более кратким?

5

Решение

Вы используете 2D-распределитель памяти для массива ядра. Ядра массивов представляют собой плотные одномерные массивы, а не двумерные пошаговые массивы, как типичное изображение АЭС.

Просто замените 2D CALLA malloc простым Cuda Malloc размером kernelWidth * kernelHeight * sizeof (Npp32s) и сделайте обычную 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).

2

Другие решения

Других решений пока нет …