CUDA изображение не показывает вывод - PullRequest
0 голосов
/ 15 декабря 2018

Это код для переворачивания изображения с использованием ядра CUDA и opencv для чтения и показа изображения. В основной функции была показана картинка ввода, но вывод отображается как черное окно.Кстати, в коде нет ошибок, он может компилироваться и запускаться, но вывод выглядит weid.Ниже то, что я пробовал до сих пор.

#include< iostream>

#include< cstdio>

#include < opencv2/core.hpp>

#include < opencv2/imgcodecs.hpp>

#include < opencv2/highgui.hpp>

#include< cuda_runtime.h >


using std::cout;
using std::endl;


__global__ void mirror( unsigned char* input, unsigned char* output, int numRows, int numCols)
{
//2D Index of current thread
const int col = blockIdx.x * blockDim.x + threadIdx.x;

const int row = blockIdx.y * blockDim.y + threadIdx.y;


if ( col >= numCols || row >= numRows )

{

 return;

}

int thread_x = blockDim.x * blockIdx.x + threadIdx.x;

int thread_y = blockDim.y * blockIdx.y + threadIdx.y;

    int thread_x_new = numCols-thread_x;

    int thread_y_new = thread_y;

int mId = thread_y * numCols + thread_x;

    int mId_new = thread_y_new * numCols + thread_x_new;

    output[mId_new] = input[mId]; 
}

 void convert_to_mirror(const cv::Mat& input, cv::Mat& output,int numrows,int numcols)
{

const dim3 blockSize(1024,1,1);
int a=numcols/blockSize.x, b=numrows/blockSize.y;   
const dim3 gridSize(a+1,b+1,1);
const size_t numPixels = numrows * numcols;
unsigned char *d_input, *d_output;

cudaMalloc<unsigned char>(&d_input, numPixels);
cudaMalloc<unsigned char>(&d_output,numPixels);
//Copy data from OpenCV input image to device memory
cudaMemcpy(d_input,input.ptr(), numPixels,cudaMemcpyHostToDevice);
//Call mirror kernel.
mirror<<<gridSize, blockSize>>>(d_input,d_output, numrows, numcols);
cudaDeviceSynchronize(); 
//copy output from device to host
cudaMemcpy(output.ptr(), d_output,numPixels, cudaMemcpyDeviceToHost);

cudaFree(d_input);

cudaFree(d_output);
}

 int main()
 {
//Read input image from the disk
cv::Mat input = cv::imread("C:/a.jpg", cv::IMREAD_COLOR);
const int rows = input.rows;

const int cols = input.cols;
if(input.empty())
{
    std::cout<<"Image Not Found!"<<std::endl;
    std::cin.get();
    return -1;
}

//Create output image
cv::Mat output(rows,cols,CV_8UC3);

//Call the wrapper function
convert_to_mirror(input,output,rows,cols);

//Show the input and output
cv::imshow("Input",input);
cv::imshow("Output",output);

//Wait for key press
cv::waitKey();

return 0;
 }

Ответы [ 2 ]

0 голосов
/ 17 декабря 2018

TLDR: Проблема связана с количеством памяти устройства, выделенной для изображения, и схемой индексации, используемой для доступа к значениям пикселей внутри ядра.Используйте исправленную реализацию из последнего раздела кода этого ответа.

Ниже приведено объяснение проблемных аспектов предоставленной реализации.

1.Общее количество байтов изображения

Входное изображение является 8-битным RGB-изображением, поэтому теоретическое число байтов, занимаемых им, равно width x height x number_of_channels.В этом случае оно должно быть numRows * numCols * 3.Но практически OpenCV выделяет выровненную память для данных изображения , поэтому общее количество байтов изображения должно быть рассчитано как image.step * numrows независимо от типа изображения и количества каналов.При этом вызовы cudaMalloc и cudaMemcpy ожидают общее количество байтов, которые мы хотим выделить или скопировать соответственно.Исправьте вызовы следующим образом (адаптируя код из ответа @ micehlson):

const size_t numBytes = input.step * numrows;
cudaMalloc<unsigned char>(&d_input, numBytes);
                                    ^
cudaMalloc<unsigned char>(&d_output, numBytes);
                                    ^

//Copy data from OpenCV input image to device memory
cudaMemcpy(d_input, input.ptr(), numBytes, cudaMemcpyHostToDevice);
                                 ^

//copy output from device to host
cudaMemcpy(output.ptr(), d_output, numBytes, cudaMemcpyDeviceToHost);
                                   ^

2.Вычисление индекса пикселя в ядре

Поскольку память изображения выровнена, фактический индекс пикселя должен быть рассчитан с использованием параметра step объекта Mat.Общая формула для расчета начального индекса пикселя в OpenCV Mat выглядит следующим образом:

index = строка * step / bytes_per_pixel_component + (каналы * столбец)

Для 8-битного изображения RGB число байтов, занимаемых одним компонентом пикселя RGB, составляет 1 байт.Это означает, что отдельный R или G или B занимает 8 байтов, в то время как весь пиксель RGB составляет 24 байта.Таким образом, начальный индекс рассчитывается как

int index = row * step + 3 * column;

Поскольку это начальный индекс, доступ к каждому отдельному каналу этого конкретного пикселя можно получить путем увеличения этого индекса до количества каналов следующим образом:

int R = index;
int G = index + 1;
int B = index + 2;

Впоследствии индекс пикселя в перевернутом изображении можно рассчитать следующим образом (при условии переворота вокруг оси y):

int flipped_index = row * step + 3 * (numCols - column - 1);

Конечно, нам потребуется шаг изображения какаргумент ядру.

Конечное ядро ​​может выглядеть так:

__global__ void mirror( unsigned char* input, unsigned char* output, int numRows, int numCols, int channels, int step)
{
    //2D Index of current thread
    const int col = blockIdx.x * blockDim.x + threadIdx.x;
    const int row = blockIdx.y * blockDim.y + threadIdx.y;

    if ( col >= numCols || row >= numRows ) return;

    const int tid = row * step + (channels * col);
    const int tid_flipped = row * step + (channels * (numCols - col - 1)); //Flip about y axis

    //Copy each component of the current pixel
    for(int i=0; i<channels; i++)
        output[tid_flipped + i] = input[tid + i]; 
}

Со всеми исправлениями финальный код может выглядеть так:

#include<iostream>
#include<cstdio>
#include<opencv2/core.hpp>
#include<opencv2/imgcodecs.hpp>
#include<opencv2/highgui.hpp>
#include<cuda_runtime.h>

using std::cout;
using std::endl;    

__global__ void mirror( unsigned char* input, unsigned char* output, int numRows, int numCols, int channels, int step)
{
    //2D index of current thread
    const int col = blockIdx.x * blockDim.x + threadIdx.x;
    const int row = blockIdx.y * blockDim.y + threadIdx.y;

    if ( col >= numCols || row >= numRows ) return;

    const int tid = row * step + (3 * col);
    const int tid_new = row * step + (3 * (numCols - col - 1)); //Flip about y axis

    //Copy each component of the current pixel
    for(int i=0; i<channels; i++)
        output[tid_new + i] = input[tid + i]; 
}

 void convert_to_mirror(const cv::Mat& input, cv::Mat& output,int numrows,int numcols)
{
    const dim3 blockSize(1024,1,1);

    int a=numcols/blockSize.x, b=numrows/blockSize.y;   

    const dim3 gridSize(a+1,b+1,1);

    const size_t numBytes = input.step * input.rows;

    unsigned char *d_input, *d_output;

    cudaMalloc<unsigned char>(&d_input, numBytes);
    cudaMalloc<unsigned char>(&d_output,numBytes);

    //Copy data from OpenCV input image to device memory
    cudaMemcpy(d_input,input.ptr(), numBytes, cudaMemcpyHostToDevice);

    //Call mirror kernel.
    mirror<<<gridSize, blockSize>>>(d_input,d_output, numrows, numcols, input.channels(), input.step);

    assert(cudaSuccess == cudaDeviceSynchronize()); 

    //copy output from device to host
    cudaMemcpy(output.ptr(), d_output,numBytes, cudaMemcpyDeviceToHost);

    cudaFree(d_input);

    cudaFree(d_output);
}

 int main()
 {
    //Read input image from the disk
    cv::Mat input = cv::imread("C:/a.jpg", cv::IMREAD_COLOR);
    const int rows = input.rows;
    const int cols = input.cols;

    if(input.empty())
    {
        std::cout<<"Image Not Found!"<<std::endl;
        std::cin.get();
        return -1;
    }

    //Create output image
    cv::Mat output(rows,cols,CV_8UC3);

    //Call the wrapper function
    convert_to_mirror(input,output,rows,cols);

    //Show the input and output
    cv::imshow("Input",input);
    cv::imshow("Output",output);

    //Wait for key press
    cv::waitKey();

    return 0;
 }

Скомпилировано с помощью следующей команды:

nvcc -o mirror -std = c ++ 11 mirror.cu -I / usr / local / include / opencv4 -L / usr/ local / lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui

Протестировано с OpenCV 4.0 и CUDA 9 в Ubuntu 16.04

0 голосов
/ 15 декабря 2018

TLDR; OpenCV уже имеет такую ​​функциональность, в том числе и в виде графического процессора: cv :: cuda :: flip и называется так: cv::cuda::flip(input, output, 1);

First ofвсе, вы используете цветное изображение - CV_8UC3 - это означает, что один пиксель - это не unsigned char, как вы написали, а cv::Vec3b.Так что это uchar для каждого из цветов R, G, B.Это требует некоторых настроек в коде:

__global__ void mirror(unsigned char* input, unsigned char* output, int numRows, int numCols)
{
    const int col = blockIdx.x * blockDim.x + threadIdx.x;
    const int row = blockIdx.y * blockDim.y + threadIdx.y;

    if(col >= numCols || row >= numRows) return;

    int mirrorCol = numCols - col;

    int idx = row * numCols * 3 + col * 3;
    int mirrorIdx = row * numCols * 3 + mirrorCol * 3;

    output[mirrorIdx] = input[idx]; //R
    output[mirrorIdx + 1] = input[idx + 1]; //G
    output[mirrorIdx + 2] = input[idx + 2]; //B
}

void convert_to_mirror(const cv::Mat& input, cv::Mat& output, int numrows, int numcols)
{
    const dim3 blockSize(1024, 1, 1);
    int a = numcols / blockSize.x, b = numrows / blockSize.y;
    const dim3 gridSize(a + 1, b + 1, 1);
    const size_t numPixels = numrows * numcols;
    const size_t numBytes = numPixels * 3; // <----- to transfer all channels R,G,B
    unsigned char *d_input, *d_output;

    cudaMalloc<unsigned char>(&d_input, numBytes);  
    cudaMalloc<unsigned char>(&d_output, numBytes); 

    //Copy data from OpenCV input image to device memory
    cudaMemcpy(d_input, input.ptr(), numBytes, cudaMemcpyHostToDevice);

    //Call mirror kernel.
    mirror << <gridSize, blockSize >> > (d_input, d_output, numrows, numcols);
    cudaDeviceSynchronize();
    //copy output from device to host
    cudaMemcpy(output.ptr(), d_output, numBytes, cudaMemcpyDeviceToHost);

    cudaFree(d_input);
    cudaFree(d_output);
}

Также, если вы хотите обрабатывать изображения на графическом процессоре, вы можете захотеть взглянуть на класс GpuMat или для ручного доступа к памяти изображений, уже инкапсулируя пиксельтип - PtrStep

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...