CUDA图像不显示输出

时间:2018-12-15 09:17:32

标签: c++ image opencv cuda

这是使用CUDA内核和opencv翻转并读取图像的代码,在主函数中,显示了输入的图片,但输出却显示为黑色窗口。顺便说一句,代码上没有错误,它可以编译和运行,但是输出看起来很奇怪。下面是我到目前为止尝试过的。

#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 :(得分:2)

TLDR:问题在于为图像分配的设备内存量以及用于访问内核内部像素值的索引方案。使用此答案最后代码部分中纠正的实现。

以下是对所提供实现的问题方面的说明。

1。图片字节总数

输入图像是8位RGB图像,因此,理论上占据的字节数等于width x height x number_of_channels。在这种情况下,它应该是numRows * numCols * 3。但是实际上,OpenCV分配了aligned memory for image data,因此无论图像类型和通道数如何,图像字节的总数应计算为image.step * numrows。话虽这么说,cudaMalloccudaMemcpy调用期望我们分别要分配或复制的字节总数。如下纠正呼叫(从@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。内核中的像素索引计算

由于图像存储器已对齐,因此应使用Mat对象的step参数计算像素的实际索引。用于计算OpenCV Mat中像素的起始索引的通用公式如下:

索引=行* 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);

当然,我们需要将image步骤作为内核的参数。

最终的内核可能看起来像这样:

__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

在Ubuntu 16.04上使用OpenCV 4.0和CUDA 9进行了测试

答案 1 :(得分:1)

TLDR; OpenCV已经具有此类功能,也具有GPU风格:cv::cuda::flip 并像cv::cuda::flip(input, output, 1);

这样称呼它

首先,您正在使用彩色图像-CV_8UC3-表示单个像素不是您编写的unsigned char,而是cv::Vec3b。因此,每种R,G,B颜色均为uchar。这需要对代码进行一些调整:

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

此外,如果您想在GPU上处理图像,则可能需要查看GpuMat class或进行手动图像内存访问,该操作已经封装了像素类型-PtrStep