Свертка OpenCL Image не работает (переполнение стека) - PullRequest
0 голосов
/ 28 мая 2019

Я довольно новичок в программировании с OpenCL в целом, и английский не является моим родным языком, поэтому, пожалуйста, извините за любые ошибки.

Я пытаюсь загрузить входное изображение с помощью OpenCV и выполнить свертку изображения с помощью фильтра. Расчеты выполняются на GPU с OpenCL.

Подход состоял в том, чтобы бегать по изображениям и извлекать значения пикселей с помощью функции .at CV и записывать значения пикселей в 1D-массивы. При передаче этих массивов на GPU-устройство появляется сообщение об ошибке переполнения стека.

Любые комментарии были бы хорошими. Заранее спасибо.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <fstream>
#include <CL/cl.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

#define PROGRAM_FILE "convolution.cl"
#define KERNEL_FUNC "convolution"

int main()
{
    //Read image to host
    cv::Mat Input;
    Input = cv::imread("simpsons.jpg", CV_LOAD_IMAGE_GRAYSCALE);

    //Rows and columns of input image
    int imageHeight, imageWidth, imageSize;
    imageHeight = Input.rows;   //768
    imageWidth = Input.cols;    //1024
    imageSize = imageHeight * imageWidth;   //1024*768

    //Extract Pixel values
    int Input_Array[1024 * 768];
    int Idx = 0;

    for (int i = 0; i < imageHeight; i++)
    {
        for (int j = 0; j < imageWidth; j++)
        {
            int pixel = Input.at<int>(i, j);
            Input_Array[Idx] = pixel;
            Idx++;
        }
    }

    //Create output array;
    int Output_Array[1024 * 768];

    //45 degree motion blur
    int filter[49] =
    {   0,      0,      0,      0,      0,      0,      0,
        0,      0,      0,      0,      0,      0,      0,
        0,      0,     -1,      0,      1,      0,      0,
        0,      0,     -2,      0,      2,      0,      0,
        0,      0,     -1,      0,      1,      0,      0,
        0,      0,      0,      0,      0,      0,      0,
        0,      0,      0,      0,      0,      0,      0 };

    //Convolution filter is 7x7
    int filterWidth = 7;
    int filterSize = filterWidth * filterWidth;     //Assuming square filter

    //Setup OpenCL Structure
    //Platform;
    int err;
    std::vector<cl::Platform> platform;
    cl::Platform::get(&platform);

    //Device
    std::vector<cl::Device> device;
    platform[0].getDevices(CL_DEVICE_TYPE_GPU, &device);

    //Context
    cl::Context context(device, NULL, NULL, NULL, NULL);

    //Command Queue
    cl::CommandQueue queue(context, device[0], 0);

    //Instead of Working with OpenCL images we use OpenCL Buffers instead
    //Create space for the 7x7 filter on the device
    cl::Buffer d_Filter(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, filterSize * sizeof(cl_int), NULL, &err);

    //Create space for the input and output images on the device
    cl::Buffer d_Input(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, filterSize * sizeof(cl_int), NULL, &err);
    cl::Buffer d_Output(context, CL_MEM_WRITE_ONLY, filterSize * sizeof(cl_int), NULL, &err);

    //Copy the filter buffer to device
    queue.enqueueWriteBuffer(d_Filter, CL_FALSE, 0, filterSize * sizeof(cl_int), filter, NULL, NULL);

    //Copy image buffer to device
    queue.enqueueWriteBuffer(d_Input, CL_FALSE, 0, imageSize * sizeof(cl_int), Input_Array, NULL, NULL);

    //Program
    std::ifstream programFile(PROGRAM_FILE);
    std::string programString(std::istreambuf_iterator<char>(programFile), (std::istreambuf_iterator<char>()));
    cl::Program::Sources source(1, std::make_pair(programString.c_str(), programString.length() + 1));
    cl::Program program(context, source, &err);
    err = program.build(device);

    //Create Kernel
    cl::Kernel kernel(program, KERNEL_FUNC, &err);

    //Set Kernel Arguments
    kernel.setArg(0, &d_Input);
    kernel.setArg(1, &d_Output);
    kernel.setArg(2, &imageHeight);
    kernel.setArg(3, &imageWidth);
    kernel.setArg(4, &d_Filter);
    kernel.setArg(5, &filterWidth);

    //Execute Kernel
    cl::NDRange offset(0, 0);
    cl::NDRange global_size(imageHeight, imageWidth);       //1.height - 2.width
    cl::NDRange local_size(1, 1);
    queue.enqueueNDRangeKernel(kernel,
        offset,
        global_size,
        local_size);

    //Read back the Output
    queue.enqueueReadBuffer(d_Output, CL_TRUE, 0, imageSize * sizeof(cl_int), Output_Array, NULL, NULL);

    //Convert 1D_Output to 2D_Output
    int Output_2DArray[768][1024];
    Idx = 0;
    for (int i = 0; i < imageHeight; i++)
    {
        for (int j = 0; j < imageWidth; j++)
        {
            Output_2DArray[i][j] = Input_Array[Idx];
            Idx++;
        }
    }

    //Create mat from Output_2DArray
    cv::Mat Output(768, 1024, CV_8U, Output_2DArray);

    cv::namedWindow("Result", CV_WINDOW_AUTOSIZE);

    cv::imshow("Output", Output);

    cv::waitKey();

}

Kernel Code:
__kernel void convolution(
   __global int* InputImage,
   __global int* OutputImage, 
   int h,
   int w,
   __constant float* int, 
   int filterWidth) 
{
   // Store each work-item’s unique row and column
   int column = get_global_id(0);
   int row    = get_global_id(1);

   int halfWidth = filterWidth/2;

   // Ignore Border Pixels
   if (row - halfWidth < 0 || row + halfWidth > h || column - halfWidth < 0 || column + halfWidth > w) {
        OutputImage[row*w + column] = 0     /*InputImage[row*w + column]*/;
        return;
   }

    // Iterator for the filter
   int filterIdx = 0;
   summ = 0;

   //Iterate over rows
   for (int j = -halfWidth; j <= halfWidth; j++) 
   {
      //Iterate over columns
      for (int i = -halfWidth; i <= halfWidth; i++) 
      {
         summ += InputImage[(row + j)*w + column + i]*filter[filterIdx++];
      }
   }

    OutputImage[row*w + column] = summ;
}
...