OpenCV CUDA C++ C Image Gray - c++

I am new Here...
I need help in the following code..
I am a beginner in coding.
I am trying to convert a color image in .bmp format to gray scale image using CUDA and openCV
Can anyone find the error or bug or the mistake which i have done in my code and help me.
I am also attaching the input i used and the output(screenshot - Image name output) which i got from the code(image in my code).. In the screenshot the image in the background is the original image. You can use whatever image you want.
#include "cuda_runtime.h"
#include <iostream>
#include <ctime>
#include <stdio.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
__global__ void convertImage(int width, int height, int nchannels, int step, uchar *d_data, int nchannels2, int step2, uchar *d_data2)
{
int i, j, r, g, b, byte, z = 0;
for(i=0; i<height; i++)
for(j=0; j<width; j++)
{
r = d_data[i*step + j*nchannels + 0];
g = d_data[i*step + j*nchannels + 1];
b = d_data[i*step + j*nchannels + 2];
byte = (r+g+b)/3;
d_data2[i*step2 + j*nchannels2 + 0] = byte;
d_data2[i*step2 + j*nchannels2 + 1] = byte;
d_data2[i*step2 + j*nchannels2 + 2] = byte;
}
}
int main()
{
IplImage *img = cvLoadImage("D://1.bmp", CV_LOAD_IMAGE_COLOR);
int width = img->width;
int height = img->height;
int nchannels = img->nChannels;
int step = img->widthStep;
cout<<"Image 1 : "<<width<<"\t"<<height<<"\t"<<nchannels<<"\t"<<step<<endl;
uchar *data = (uchar*)img->imageData;
uchar *d_data;
int size = sizeof(data);
cudaMalloc(&d_data, size);
cudaMemcpy(d_data, data, size, cudaMemcpyHostToDevice);
IplImage *img2 = cvCreateImage(cvSize(img->height, img->width), IPL_DEPTH_8U, 1);
int width2 = img2->width;
int height2 = img2->height;
int nchannels2 = img2->nChannels;
int step2 = img2->widthStep;
cout<<"Image 2 : "<<width2<<"\t"<<height2<<"\t"<<nchannels2<<"\t"<<step2<<endl;
uchar *data2 = (uchar*)img2->imageData;
uchar *d_data2;
int size2 = sizeof(data2);
cudaMalloc(&d_data2, size2);
long long i;
uchar *temp = data;
convertImage<<<1,1>>>(width, height, nchannels, step, d_data, nchannels2, step2, d_data2);
cudaMemcpy(data2, d_data2, sizeof(data2), cudaMemcpyHostToDevice);
cvNamedWindow("Imagecolor");
cvShowImage("Imagecolor", img);
cvNamedWindow("Gray");
cvShowImage("Gray", img2);
cvWaitKey();
return 0;
}

There a lot of problems with the code!
1: The size of device memory being allocated:
int size = sizeof(data);
sizeof(data) will return the size of pointer on the current platform. Which is most likely 4 or 8 bytes. So you are allocating a maximum of 8 bytes of device memory and copying whole image into it!!!
The actual number of bytes of the image should be calculated as:
int size = step * height;
and
int size2 = step2 * height2;
2: Direction flag and data size of second cudaMemcpy call:
As pointed out in another answer...
cudaMemcpy(data2, d_data2, sizeof(data2), cudaMemcpyHostToDevice);
should be
cudaMemcpy(data2, d_data2, size2, cudaMemcpyDeviceToHost);
3: Type of output image
In the kernel, 3 values are being written to the output in each iteration, while the output image has a single channel. Either write only one value to the output, or create the output image with 3 channels.
The cvSize function should be called as cvSize(width,height) instead of cvSize(height, width) when creating img2.
Also, the kernel is being launched with 1 thread only, and most likely, it would trigger an execution timeout error if the image size is large.

One thing that I can point out by looking at your code is that in the second call to cudaMemcpy (after convertImage) you should pass in 'cudaMemcpyDeviceToHost' as the flag and NOT 'cudaMemcpyHostToDevice'. You want to get the converted image back from the card.
I am not sure if this will be all that it will take to get your program working.

Related

uchar* to std::vector<uchar> and back?

I have some image data as a uchar*. I need to run processing on it as a std::vector<uchar>, and then convert it back. I am using this code:
unsigned char* buffer = inputImg.data; //Image data from cv::Mat
std::vector<uchar> vec;
size_t size_of_buffer = sizeof(buffer);
vec.assign(buffer, buffer + size_of_buffer);
uchar* _compressed = reinterpret_cast<uchar*>(vec.data());
When I then view the result with:
cv::Mat mat = cv::Mat(_height, _width, inputImg.type(), _compressed );
this results in a black image. Where am I going wrong?
EDIT:
based on comments below, i have changed the code to:
//from Mat
int COLOR_COMPONENTS = inputImg.channels();
int _width = inputImg.cols;
int _height = inputImg.rows;
//to std::vector and back
std::vector<uchar> vec;
size_t size_of_buffer = _width * _height*COLOR_COMPONENTS;
vec.assign(buffer, buffer + size_of_buffer);
uchar* _compressed = reinterpret_cast<uchar*>(vec.data());
As in the answer below, this works.
This code works for me and displays the image correctly:
int main()
{
cv::Mat input = cv::imread("C:/StackOverflow/Input/Lenna.png");
cv::Mat inputImg = input;
int COLOR_COMPONENTS = inputImg.channels();
int _width = inputImg.cols;
int _height = inputImg.rows;
//to std::vector and back
std::vector<uchar> vec;
size_t size_of_buffer = _width * _height*COLOR_COMPONENTS;
unsigned char* buffer = inputImg.data;
vec.assign(buffer, buffer + size_of_buffer);
uchar* _compressed = reinterpret_cast<uchar*>(vec.data());
cv::Mat mat = cv::Mat(_height, _width, inputImg.type(), _compressed);
cv::imshow("output", mat);
cv::waitKey(0);
return 0;
}
Visual Studio 2013 with OpenCV 3.4
sizeof(buffer) yields the size of the pointer to the buffer, not the amount of data inside the buffer. You must get the buffer size from somewhere else.

Make 32x32 sections on an image in C++ OpenCV?

I want to take a gray scaled image and divide it into 32x32 sections. Each section will contain pixels and based their intensity and volume, they would be considered a 1 or a 0.
My thought is that I would name the sections like "(x,y)". For example:
Section(1,1) contains this many pixels that are within this range of intensity so this is a 1.
Does that make sense? I tried looking for the answer to this question but dividing up the image into overlaying sections doesn't seem to yield any results in the OpenCV community. Keep in mind I don't want to change the way the image looks, just divide it up into a 32x32 table with (x,y) being a "section" of the picture.
Yes you can do that. Here is the code. It is rough around the edges, but it does what you request. See comments in the code for explanations.
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
struct BradleysImage
{
int rows;
int cols;
cv::Mat data;
int intensity_threshold;
int count_threshold;
cv::Mat buff = cv::Mat(32, 32, CV_8UC1);
// When we call the operator with arguments y and x, we check
// the region(y,x). We then count the number of pixels within
// that region that are greater than some threshold. If the
// count is greater than desired number, we return 255, else 0.
int operator()(int y, int x) const
{
int j = y*32;
int i = x*32;
auto window = cv::Rect(i, j, 32, 32);
// threshold window contents
cv::threshold(data(window), buff, intensity_threshold, 1, CV_THRESH_BINARY);
int num_over_threshold = cv::countNonZero(buff);
return num_over_threshold > count_threshold ? 255 : 0;
}
};
int main() {
// Input image
cv::Mat img = cv::imread("walken.jpg", CV_8UC1);
// I resize it so that I get dimensions divisible
// by 32 and get better looking result
cv::Mat resized;
cv::resize(img, resized, cv::Size(3200, 3200));
BradleysImage b; // I had no idea how to name this so I used your nick
b.rows = resized.rows / 32;
b.cols = resized.cols / 32;
b.data = resized;
b.intensity_threshold = 128; // just some threshold
b.count_threshold = 512;
cv::Mat result(b.rows -1, b.cols-1, CV_8UC1);
for(int y = 0; y < result.rows; ++y)
for(int x = 0; x < result.cols; ++x)
result.at<uint8_t>(y, x) = b(y, x);
imwrite("walken.png", result);
return 0;
}
I used Christopher Walken's image from Wikipedia and obtained this result:

Mat Image creation from raw buffer data

I have float x, float y, float z values of an image. I want to construct a 16 bit png depth image by copying the z values. The image I am getting as a result has some invalid points. Below is my code.
uint16_t* depthValues = new uint16_t[size];
auto sampleVector(DepthPoints);
for (unsigned int i = 0; i < sampleVector.size(); i++)
{
depthValues[i] = (sampleVector.at(i).z) * 65536;
}
Mat newDepthImage = cv::Mat(var.height, var.width, CV_16UC1,depthValues);
imwrite(Location, CImage);
Can someone tell me, if I can copy the float values into an unsigned char array to create the image?
Is that why my image has invalid points?
auto sampleVector(DepthPoints);
const int size = sampleVector.size();
float* depthValues = new float[size];
for (unsigned int i = 0; i < sampleVector.size(); i++)
{
depthValues[i] = (sampleVector.at(i).z);
}
Mat depthImageOne, depthImageTwo;
Mat depthImageNew = cv::Mat(var.height, var.width, CV_32FC1,depthValues);
normalize(newDepthImageNew, depthImageOne, 1, 0, NORM_MINMAX, CV_32FC1);
depthImageOne.convertTo(depthImageTwo, CV_16UC1, 65536.0,0.0);
imwrite("path", depthImageTwo);
Normalization might cause lose of depth information. I have used normalization for visualization of the images. To preserve the depth information, I used the below code.
Mat depthImageNew = cv::Mat(var.height, var.width, CV_32FC1,depthValues);
depthImageOne.convertTo(depthImageTwo, CV_16UC1, 1000.0,0.0);

CUDA error with processing the image

I'm trying to get black and white image as the output with color image as the input. I'm using an OpenCV to get the image and write the output, and CUDA to make the image black and white in kernel. I tried the same code, but without OpenCV, and it worked fine. But now the output is slightly different from what I really expect to get.
I think that CUDA code needs some modification to work with OpenCV. I worked a bit with it but failed to find the way to do that. Maybe somebody can give me an advice or modify my code, please? I'm really confused with this problem.
__global__ void addMatrix(uchar4 *DataIn, unsigned char *DataOut)
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
DataOut[idx] = (DataIn[idx].x + DataIn[idx].y + DataIn[idx].z)/3;
}
int main()
{
cudaDeviceProp deviceProp;
cudaGetDeviceProperties(&deviceProp, 0);
char* c = "";
printf("Input source of image\n Example of right directory file: E:\henrik-evensen-castle-valley-v03.jpg\n Your turn:\n");
char *tbLEN;
tbLEN = new char [1024];
cin.getline(tbLEN,1024);
cout<< endl << "Your image: " << tbLEN << endl;
//Data for input image
IplImage* image;
image = cvLoadImage(tbLEN, 1);
int height = image->height;
int width = image->width;
int step = image->widthStep;
int SizeIn = (step*height);
printf("\nProcessing image\n");
//Data for output image
IplImage *image2 = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
int step2 = image2->widthStep;
int SizeOut = step2 * height;
//GPU
uchar4* DatIn = (uchar4*)image->imageData;
unsigned char* DatOut = (unsigned char*)image2->imageData;
uchar4 *datIndev;
unsigned char *datOutdev;
printf("Allocating memory on Device\n");
/* Allocate memory on Device */
cudaMalloc(&datIndev, SizeIn * sizeof(unsigned char));
cudaMalloc(&datOutdev, SizeOut * sizeof(unsigned char));
printf("Copy data on Device\n");
/* Copy data on Device */
cudaMemcpy(datIndev, DatIn, SizeIn * sizeof(unsigned char), cudaMemcpyHostToDevice);
cudaMemcpy(datOutdev, DatOut, SizeOut * sizeof(unsigned char), cudaMemcpyHostToDevice);
int NumThreadsX = deviceProp.maxThreadsPerBlock;
int NumBlocksX = (width * height)/NumThreadsX;
dim3 blocks(NumBlocksX, 1, 1);
dim3 threads(NumThreadsX, 1, 1);
addMatrix <<< blocks, threads >>> (datIndev, datOutdev);
cudaMemcpy(DatOut, datOutdev, SizeOut * sizeof(unsigned char), cudaMemcpyDeviceToHost);
cvNamedWindow("Imagecolor");
cvShowImage("Imagecolor", image);
cvNamedWindow("Gray");
cvShowImage("Gray", image2);
const char* filename1 = "CcPwSwMW4AELPUc.jpg";
printf("Saving an output image\n");
cvSaveImage( filename1, image2 );
cudaFree(datOutdev);
cudaFree(datIndev);
cvWaitKey(0);
return 0;
}
There are several problems here:
Your assumption about four channel data is incorrect. Your code will load a three channel BGR image into memory from file. So you need to change references from uchar4 to ucharand then have each thread load three bytes from the source image within your kernel
Your kernel itself contains a potential arithmetic error. The sum of three unsigned char pixel values can overflow an unsigned char intermediate result and produce an incorrect average. You should use a larger type for the calculation.
Taken together, your kernel should look something like this:
__global__ void addMatrix(unsigned char *DataIn, unsigned char *DataOut)
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
int b = DataIn[3*idx];
int g = DataIn[3*idx+1];
int r = DataIn[3*idx+2];
DataOut[idx] = (unsigned char)((b + r + g)/3);
}
Then you might find your image looks correct.

opencv - rgb value keeps changing

I'm having some problem with the rgb or in opencv bgr
What I'm trying to do is find the overall value of the bgr of a certain pictures
but every time I run the program without changing anything with exact same pictures the values of the bgr keeps changing..
This is how i coded to find the values of bgr
#include <opencv\cv.h>
#include <opencv\highgui.h>
using namespace std;
char path[255];
int main( int argc, char** argv )
{
IplImage *red[50];
IplImage *green[50];
IplImage *blue[50];
for(int i = 1; i <= 50; i++)
{
IplImage *img;
sprintf(path, "C:\\picture (%01).bmp", i);
img = cvLoadImage(path);
red[i] = cvCreateImage(cvGetSize(img), 8, 1);
green[i] = cvCreateImage(cvGetSize(img), 8, 1);
blue[i] = cvCreateImage(cvGetSize(img), 8, 1);
cvSplit(img, blue[i], green[i], red[i], NULL);
cvReleaseImage(&img);
int total = (int)(blue[i]) + (int)(green[i]) + (int)(red[i]);
cout << total << endl;
cvWaitKey(1);
}
cvWaitKey(0);
return 0;
}
I am not sure how did you achieve type casting from IplImage to int
int total = (int)(blue[i]) + (int)(green[i]) + (int)(red[i]);
but you certainly need to use pixel by pixel summation for each channel (not image by image) to find the overall values.
Try using two loops instead of one, one for loading the images and the other for performing your operation, and let me know if it works..