├── README.md ├── calc.cpp ├── greyscale.cu ├── hw.cpp ├── main.cpp └── rankedOrderFilter.cpp /README.md: -------------------------------------------------------------------------------- 1 | cuda 2 | ==== 3 | // Color to Greyscale Conversion 4 | 5 | //A common way to represent color images is known as RGBA - the color 6 | //is specified by how much Red, Grean and Blue is in it. 7 | //The 'A' stands for Alpha and is used for transparency, it will be 8 | //ignored in this homework. 9 | 10 | //Each channel Red, Blue, Green and Alpha is represented by one byte. 11 | //Since we are using one byte for each color there are 256 different 12 | //possible values for each color. This means we use 4 bytes per pixel. 13 | 14 | //Greyscale images are represented by a single intensity value per pixel 15 | //which is one byte in size. 16 | 17 | //To convert an image from color to grayscale one simple method is to 18 | //set the intensity to the average of the RGB channels. But we will 19 | //use a more sophisticated method that takes into account how the eye 20 | //perceives color and weights the channels unequally. 21 | 22 | //The eye responds most strongly to green followed by red and then blue. 23 | //The NTSC (National Television System Committee) recommends the following 24 | //formula for color to greyscale conversion: 25 | 26 | //I = .299f * R + .587f * G + .114f * B 27 | 28 | //Notice the trailing f's on the numbers which indicate that they are 29 | //single precision floating point constants and not double precision 30 | //constants. 31 | -------------------------------------------------------------------------------- /calc.cpp: -------------------------------------------------------------------------------- 1 | void referenceCalculation(const uchar4* const rgbaImage, 2 | unsigned char *const greyImage, 3 | size_t numRows, 4 | size_t numCols) 5 | { 6 | for (size_t r = 0; r < numRows; ++r) { 7 | for (size_t c = 0; c < numCols; ++c) { 8 | uchar4 rgba = rgbaImage[r * numCols + c]; 9 | float channelSum = .299f * rgba.x + .587f * rgba.y + .114f * rgba.z; 10 | greyImage[r * numCols + c] = channelSum; 11 | } 12 | } 13 | 14 | //now that the grey-scale image is complete clear noise 15 | for(size_t r = 0; r < numRows; ++r) { //for each row 16 | for(size_t c = 0; c < numCols; ++c) { //for each column 17 | vector xi; //make a temporary list-vector 18 | for(int k=r-5/2;k<=r+5/2;k++) { //apply the window specified by x and y 19 | for(int m=c-5/2;m<=c+5/2;m++) { 20 | if((k<0)||(m<0)) xi.push_back(0); //on edges of the image use 0 values 21 | else xi.push_back(greyImage[k * numCols + m]); 22 | } 23 | } 24 | std::sort(std::begin(xi),std::end(xi)); //sort elements of 'xi' neighbourhood vector 25 | greyImage[r * numCols + c]=xi[3]; //replace pixel with element specified by 'rank' 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /greyscale.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include "calc.cpp" 3 | #include "utils.h" 4 | #include 5 | #include 6 | 7 | __global__ 8 | void rgba_to_greyscale(const uchar4* const rgbaImage, 9 | unsigned char* const greyImage, 10 | int numRows, int numCols) 11 | { 12 | int index_x = blockIdx.x * blockDim.x + threadIdx.x; 13 | int index_y = blockIdx.y * blockDim.y + threadIdx.y; 14 | 15 | // map the two 2D indices to a single linear, 1D index 16 | int grid_width = gridDim.x * blockDim.x; 17 | int index = index_y * grid_width + index_x; 18 | 19 | vector xi; //make a temporary list-vector 20 | for(int k=index_x-5/2;k<=index_x+5/2;k++) { //apply the window specified by x and y 21 | for(int m=index_y-5/2;m<=index_y+5/2;m++) { 22 | if((k<0)||(m<0)) xi.push_back(0); //on edges of the image use 0 values 23 | else xi.push_back(rgbaImage[k * numCols + m]); 24 | } 25 | } 26 | std::sort(std::begin(xi),std::end(xi)); //sort elements of 'xi' neighbourhood vector 27 | greyImage[index]=xi[3]; //replace pixel with element specified by 'rank' (3) 28 | 29 | // write out the final result 30 | greyImage[index] = .299f * rgbaImage[index].x + .587f * rgbaImage[index].y + .114f * rgbaImage[index].z; 31 | 32 | } 33 | 34 | void your_rgba_to_greyscale(const uchar4 * const h_rgbaImage, uchar4 * const d_rgbaImage, 35 | unsigned char* const d_greyImage, size_t numRows, size_t numCols) 36 | { 37 | const int thread = 16; 38 | const dim3 blockSize( thread, thread, 1); 39 | const dim3 gridSize( ceil(numRows/(float)thread), ceil(numCols/(float)thread), 1); 40 | rgba_to_greyscale<<>>(d_rgbaImage, d_greyImage, numRows, numCols); 41 | 42 | cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError()); 43 | } 44 | -------------------------------------------------------------------------------- /hw.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | #include 6 | #include 7 | #include 8 | 9 | cv::Mat imageRGBA; 10 | cv::Mat imageGrey; 11 | 12 | uchar4 *d_rgbaImage__; 13 | unsigned char *d_greyImage__; 14 | 15 | size_t numRows() { return imageRGBA.rows; } 16 | size_t numCols() { return imageRGBA.cols; } 17 | 18 | //return types are void since any internal error will be handled by quitting 19 | //no point in returning error codes... 20 | //returns a pointer to an RGBA version of the input image 21 | //and a pointer to the single channel grey-scale output 22 | //on both the host and device 23 | void preProcess(uchar4 **inputImage, unsigned char **greyImage, 24 | uchar4 **d_rgbaImage, unsigned char **d_greyImage, 25 | const std::string &filename) { 26 | //make sure the context initializes ok 27 | checkCudaErrors(cudaFree(0)); 28 | 29 | cv::Mat image; 30 | image = cv::imread(filename.c_str(), CV_LOAD_IMAGE_COLOR); 31 | if (image.empty()) { 32 | std::cerr << "Couldn't open file: " << filename << std::endl; 33 | exit(1); 34 | } 35 | 36 | cv::cvtColor(image, imageRGBA, CV_BGR2RGBA); 37 | 38 | //allocate memory for the output 39 | imageGrey.create(image.rows, image.cols, CV_8UC1); 40 | 41 | //This shouldn't ever happen given the way the images are created 42 | //at least based upon my limited understanding of OpenCV, but better to check 43 | if (!imageRGBA.isContinuous() || !imageGrey.isContinuous()) { 44 | std::cerr << "Images aren't continuous!! Exiting." << std::endl; 45 | exit(1); 46 | } 47 | 48 | *inputImage = (uchar4 *)imageRGBA.ptr(0); 49 | *greyImage = imageGrey.ptr(0); 50 | 51 | const size_t numPixels = numRows() * numCols(); 52 | //allocate memory on the device for both input and output 53 | checkCudaErrors(cudaMalloc(d_rgbaImage, sizeof(uchar4) * numPixels)); 54 | checkCudaErrors(cudaMalloc(d_greyImage, sizeof(unsigned char) * numPixels)); 55 | checkCudaErrors(cudaMemset(*d_greyImage, 0, numPixels * sizeof(unsigned char))); //make sure no memory is left laying around 56 | 57 | //copy input array to the GPU 58 | checkCudaErrors(cudaMemcpy(*d_rgbaImage, *inputImage, sizeof(uchar4) * numPixels, cudaMemcpyHostToDevice)); 59 | 60 | d_rgbaImage__ = *d_rgbaImage; 61 | d_greyImage__ = *d_greyImage; 62 | } 63 | 64 | void postProcess(const std::string& output_file) { 65 | const int numPixels = numRows() * numCols(); 66 | //copy the output back to the host 67 | checkCudaErrors(cudaMemcpy(imageGrey.ptr(0), d_greyImage__, sizeof(unsigned char) * numPixels, cudaMemcpyDeviceToHost)); 68 | 69 | //output the image 70 | cv::imwrite(output_file.c_str(), imageGrey); 71 | 72 | //cleanup 73 | cudaFree(d_rgbaImage__); 74 | cudaFree(d_greyImage__); 75 | } 76 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "timer.h" 3 | #include "utils.h" 4 | #include 5 | #include 6 | 7 | size_t numRows(); //return # of rows in the image 8 | size_t numCols(); //return # of cols in the image 9 | 10 | void preProcess(uchar4 **h_rgbaImage, unsigned char **h_greyImage, 11 | uchar4 **d_rgbaImage, unsigned char **d_greyImage, 12 | const std::string& filename); 13 | 14 | void postProcess(const std::string& output_file); 15 | 16 | void your_rgba_to_greyscale(const uchar4 * const h_rgbaImage, uchar4 * const d_rgbaImage, 17 | unsigned char* const d_greyImage, size_t numRows, size_t numCols); 18 | 19 | //include the definitions of the above functions 20 | #include "HW.cpp" 21 | 22 | int main(int argc, char **argv) { 23 | uchar4 *h_rgbaImage, *d_rgbaImage; 24 | unsigned char *h_greyImage, *d_greyImage; 25 | 26 | std::string input_file; 27 | std::string output_file; 28 | if (argc == 3) { 29 | input_file = std::string(argv[1]); 30 | output_file = std::string(argv[2]); 31 | } 32 | else { 33 | std::cerr << "Usage: ./hw input_file output_file" << std::endl; 34 | exit(1); 35 | } 36 | //load the image and give us our input and output pointers 37 | preProcess(&h_rgbaImage, &h_greyImage, &d_rgbaImage, &d_greyImage, input_file); 38 | 39 | GpuTimer timer; 40 | timer.Start(); 41 | //call the grayscale code 42 | your_rgba_to_greyscale(h_rgbaImage, d_rgbaImage, d_greyImage, numRows(), numCols()); 43 | timer.Stop(); 44 | cudaDeviceSynchronize(); checkCudaErrors(cudaGetLastError()); 45 | printf("\n"); 46 | int err = printf("%f msecs.\n", timer.Elapsed()); 47 | 48 | if (err < 0) { 49 | //Couldn't print! Probably the student closed stdout - bad news 50 | std::cerr << "Couldn't print timing information! STDOUT Closed!" << std::endl; 51 | exit(1); 52 | } 53 | 54 | //check results and output the grey image 55 | postProcess(output_file); 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /rankedOrderFilter.cpp: -------------------------------------------------------------------------------- 1 | //function performs noise removal on image, using ranked order filter method. 2 | 3 | void RankedOrderFilter(Mat image, int x, int y, int rank) //x,y must be odd 4 | { 5 | image.convertTo(image, CV_32F); 6 | for(int channel=0;channel xi; //make a temporary list-vector 13 | for(int k=i-x/2;k<=i+x/2;k++) //apply the window specified by x and y 14 | { 15 | for(int m=j-y/2;m<=j+y/2;m++) 16 | { 17 | if((k<0)||(m<0)) xi.push_back(0); //on edges of the image use 0 values 18 | else xi.push_back(image.data[image.step[0]*i + image.step[1]* j + channel]); 19 | } 20 | } 21 | std::sort(std::begin(xi),std::end(xi)); //sort elements of 'xi' neighbourhood vector 22 | image.data[image.step[0]*i + image.step[1]* j + channel]=xi[rank]; //replace pixel with element specified by 'rank' 23 | } 24 | } 25 | } 26 | image.convertTo(image, CV_8U); 27 | } --------------------------------------------------------------------------------