Возврат с GPU uchar и преобразование его в Mat, не получается сделать корректные вычисления

Суть задачи проста: применить к заданному каналу изображения операцию "негатив".
У меня есть программа, которая решает поставленную задачу просто на cpp с использованием opencv (cpp + opencv исходные требования):

int main(int argc, char** argv)
{
    // Чтение файла
    Mat image = imread("in_img.jpg");
    if (!image.data)    
    {
        cout << "Could not open or find the image" << std::endl;
        return -1;
    }

    // Работа с изображением
    int channel = 2;                                // изменяемый канал
    for (int i = 0; i < image.rows; i++)
    {
        Vec3b* p = image.ptr<Vec3b>(i);             //указатель на 1-й пиксель в строке
        
        for (int j = 0; j < image.cols; j++)
            p[j][channel] = 255 - p[j][channel];    // применяем к заданному каналу операцию "негатив"
    }

    // Показ изображения
    namedWindow("Display window", WINDOW_AUTOSIZE); 
    imshow("Display window", image);                
    waitKey(0);                                     
    return 0;
}

Она работает.
Теперь мне необходимо переложить все на GPU (Cuda). Я все настроил, все запускается, изучил базу по cuda, но с выполнением задания получается косяк. Изображение итоговое просто черное. Не могу понять в чем проблема.
Вот код (файл .cu):


#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

#include <stdio.h>
#include <string.h>
#include <memory.h>

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

using namespace cv;
using namespace std;

#pragma region Проверка на ошибки

static void Check(cudaError_t err, const char* file, int line) {
    if (err != cudaSuccess) {
        printf("Error: %s at line %d in file %s\n", cudaGetErrorString(err), line, file);
        exit(EXIT_FAILURE);
    }
}
#define CHECK( err ) (Check( err, __FILE__, __LINE__ ))

#pragma endregion 

#pragma region Макрос для Intellisense

#ifdef __CUDACC__
#define KERNEL_ARGS2(grid, block) <<< grid, block >>>
#define KERNEL_ARGS3(grid, block, sh_mem) <<< grid, block, sh_mem >>>
#define KERNEL_ARGS4(grid, block, sh_mem, stream) <<< grid, block, sh_mem, stream >>>
#else
#define KERNEL_ARGS2(grid, block)
#define KERNEL_ARGS3(grid, block, sh_mem)
#define KERNEL_ARGS4(grid, block, sh_mem, stream)
#endif

#pragma endregion


// GPU: применение к заданному каналу изображения операции "негатив"
__global__ void negative(uchar* img, int channel, int N, uchar* out)
{
    int i = 3 * (blockIdx.x * blockDim.x + threadIdx.x);
    if (i >= N)
        return;
    out[i + channel] = 255 - img[i + channel];
}

int Launch()
{
    // Чтение файла
    Mat image = imread("in_img.jpg");
    if (!image.data)    // проверка корректного входа
    {
        cout << "Could not open or find the image" << std::endl;
        return -1;
    }
    int _width = image.cols;
    int _height = image.rows;
    int _type = image.type();

    int channel = 2;    // изменяемый канал

    uchar* host_img = image.data;

    size_t N = image.rows * image.cols * 3;

    uchar* dev_img;
    uchar* dev_imgOut;

    CHECK(cudaMalloc((void**)&dev_img, N * sizeof(uchar)));
    CHECK(cudaMalloc((void**)&dev_imgOut, N * sizeof(uchar)));

    CHECK(cudaMemcpy(dev_img, host_img, N * sizeof(uchar), cudaMemcpyHostToDevice));

    negative KERNEL_ARGS2(((N + 511) / 512), 512) (dev_img, channel, N, dev_imgOut);

    CHECK(cudaMemcpy(host_img, dev_imgOut, N * sizeof(uchar), cudaMemcpyDeviceToHost));

    Mat imageOut = Mat(_height, _width, _type, host_img);

    CHECK(cudaFree(dev_img));
    CHECK(cudaFree(dev_imgOut));

    // Показ изображения
    namedWindow("Display window", WINDOW_AUTOSIZE); // Создания окна отображения
    imshow("Display window", imageOut);             // Внутри окна показываем изображение
    waitKey(0);                                     // Ожидание нажатия клавиши в окне
    
    return 0;
}

Есть еще файл .cpp, но в нем просто main, в котором вызывается .cu.
"Почему сразу все не в .cu?" -> Я работаю через windows 10, visual studio 2019. Просто в .cu подрубить opencv не получилось, обхожу через проект .cpp.
Еще раз суть проблемы: итоговое imageOut получается просто черным. Я предполагаю, что я не верно преобразую uchar в Mat. Помогите, пожалуйста, разобраться в чем я накосячил.
p.s.: первоначально код ядра был такой:

__global__ void negative(uchar* img, int channel, int N)
{
    int i = 3 * (blockIdx.x * blockDim.x + threadIdx.x);
    if (i >= N)
        return;
    img[i + channel] = 255 - img[i + channel];
}

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


Ответы (1 шт):

Автор решения: Aivlad

К сожалению, я не могу передать словами насколько зол сам на себя и на свою невнимательность. Я первый раз делал cpp + opencv + cuda проект в одном экземпляре (были только cpp + opencv или просто cuda на cuda-c (а-ля с/с++)). Я насоздавал много проектов, в которых отрабатывал разные приемы, чтобы запустить все вместе, свой первый проект (cpp + opencv + cuda). В итоге: я не уследил за проблемой своего ноутбука проблема несоответствующей подгрузки visual studio при работе с cuda. Настроил проект как описано в решении -> все заработало!!!
p.s.: корректный код ядра таков:

__global__ void negative(uchar* img, int channel, int N)
{
    int i = 3 * (blockIdx.x * blockDim.x + threadIdx.x);
    if (i >= N)
        return;
    img[i + channel] = 255 - img[i + channel];
}

решение с 4-м параметром out - некорректное, а догадаться почему не составит труда)

→ Ссылка