0

Trying to create a simple RGB to grayscale project. Excuse the weird library, my professor refuses to update our school server, and my home computer doesn't have a Nvidia card.

#include <stdlib.h>
#include <stdio.h>
#include <cuda.h>
#include <cuda_runtime.h>
#include <math.h>
#include <string.h>
//Define and include supporting documentation. 
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

#define BLOCK_SIZE 16



__global__ void ConvertToGrayScale(unsigned char *image,unsigned char *grayimage, int height,int width,int channel,int gray_channel){

    int col = threadIdx.x + blockIdx.x * blockDim.x;
    int row = threadIdx.y + blockIdx.y * blockDim.y;

    if(row >= height || col >= width) return;

    unsigned char red=0, green=0, blue=0;
    int imageposition = row*width + col;
    int grayimageposition = row*width + col;
    imageposition = imageposition*channel;
    
    //grayimageposition = imageposition*gray_channel;


    red = image[imageposition];
    green = image[imageposition+1];
    blue = image[imageposition+2];
    grayimage[grayimageposition] = (uint8_t)(red*0.3+green*0.59+blue*0.11);

    //leave the last channel of the image the same as the grey
    if(channel==4){
        grayimage[grayimageposition + 1] = image[imageposition + 3];
    }

    //grayimage = image;
}

int main(int argc, char *argv[]){
    

    // width height and nbr of channels for image
    int width, height, channel;
    //Handle Input Image
    if (argc != 2){
        printf("Usage: ./hw5 'image.jpg'\n");
        exit(1);
    }
    
    //Load our image into a 1D array which is a linearized 2D grid where each
    //position has a pixel RGB value. so (x,y).(R,G,B)
    unsigned char *image = stbi_load(argv[1] , &width, &height, &channel, 0);
    if (image == NULL){
        printf("Could not load image.\n");
        exit(1);
    }
    //If the jpeg image has a 4th channel due to formating create this channel
    //in the grey scale image also
    int gray_channel;
    if (channel == 4){
        gray_channel = 2;
    }else{
        gray_channel = 1;
    }
    

    //Define size of our image matrices
    int graybytes = width*height*gray_channel*sizeof(unsigned char);
    int colorbytes = width*height*channel*sizeof(unsigned char);
    
    unsigned char *imagegray;
    imagegray = (unsigned char*)malloc(graybytes*gray_channel);
    
    unsigned char* d_image;
    unsigned char* d_imagegray;
    
    cudaMalloc((void**)&d_image,colorbytes);
    cudaMalloc((void**)&d_imagegray, graybytes);
    cudaMemcpy(d_image, image, colorbytes, cudaMemcpyHostToDevice);
    
    cudaMemcpy(d_imagegray, imagegray, graybytes, cudaMemcpyHostToDevice);


    
    if(!imagegray){
        printf("Could not allocate memory for the Gray Scale Image.\n");
        exit(1);
    }

    if(!image){
        printf("Could not allocate memory for the Gray Scale Image.\n");
        exit(1);
    }
    
    dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
    dim3 dimGrid((width/dimBlock.x)+1, (height/dimBlock.y)+1);


    ConvertToGrayScale<<<dimGrid, dimBlock>>>(d_image,d_imagegray,height,width,channel,gray_channel);
    cudaMemcpy( image, d_image, colorbytes*channel, cudaMemcpyDeviceToHost);
    cudaMemcpy( imagegray, d_imagegray, graybytes*channel, cudaMemcpyDeviceToHost);
    cudaThreadSynchronize();
    cudaGetLastError();

    
    
    char grey_name[30] = "gray_scale_";
    // stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality)
    stbi_write_jpg(strcat(grey_name,argv[1]), width, height, gray_channel, imagegray, 100);

    char color_name[30] = "color_scale_";
    // stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality)
    stbi_write_jpg(strcat(color_name,argv[1]), width, height, channel, image, 100);

        free(imagegray);
    stbi_image_free(image);

    cudaFree(d_image);
    cudaFree(d_imagegray);
    return 0;
}

So my output is a trash static. I believe I have isolated the issue down to how I am passing the data back and forth. When I try to trouble shoot with cuda-memcheck I get the below error twice.

Program hit cudaErrorInvalidValue (error 11) due to "invalid argument" on CUDA API call to cudaMemcpy.

I can't see what I'm doing incorrect. I also tried using cudaMallocManaged instead of manually passing the data and that just gave me a black square.

Robert Crovella
  • 143,785
  • 11
  • 213
  • 257
  • There are four cudaMemcpy calls. Couldn't you at least isolate which one is causing the error? See https://stackoverflow.com/a/14038590/681865 – talonmies May 15 '21 at 04:47
  • When you use `cudaMemcpy` to copy the data from the device back to the host (btw, I don't understand why you are copying the color image back since that is not modified by the kernel) the number of bytes argument is way too large (I'll let you figure it out -- only one channel for gray image). BTW, for good performance it is usually best to use `cudaMallocPitch` for alignment purposes. – wcochran May 15 '21 at 04:48

1 Answers1

1

I can't see what I'm doing incorrect.

When I try to trouble shoot with cuda-memcheck I get the below error twice.

Program hit cudaErrorInvalidValue (error 11) due to "invalid argument" on CUDA API call to cudaMemcpy.

You've allocated a size of colorbytes for d_image:

cudaMalloc((void**)&d_image,colorbytes);
                            ^^^^^^^^^^

but you want to transfer a size of colorbytes*channel from it?

cudaMemcpy( image, d_image, colorbytes*channel, cudaMemcpyDeviceToHost);
                            ^^^^^^^^^^^^^^^^^^

And you've allocated a size of graybytes for d_imagegray:

cudaMalloc((void**)&d_imagegray, graybytes);
                                 ^^^^^^^^^

but you want to transfer a size of graybytes*channel from it?

cudaMemcpy( imagegray, d_imagegray, graybytes*channel, cudaMemcpyDeviceToHost);
                                    ^^^^^^^^^^^^^^^^^

Neither of those look right.

Robert Crovella
  • 143,785
  • 11
  • 213
  • 257
  • This was exactly correct. I was able to figure it out. Really what I needed was sleep. Thank you for your time though. – John Crow May 19 '21 at 19:50