# Displaying Grayscale in PictureBox/PixelFormat

I am using OpenCV 2.4.6 on a Windows 8 64 bit machine with VS2012 C++/cli and trying to make a WinForms application that displays frames captured from a webcam in a picturebox. So far, this has been working for color images but when I try and display a image converted to grayscale, the displayed image seems positionally correct but kaleidoscopic color wise rather than grayscale.

void DrawCVImageDetect(System::Windows::Forms::PictureBox^ PBox, cv::Mat& colorImage)
{
System::Drawing::Graphics^ graphics = PBox->CreateGraphics();
System::IntPtr ptr(colorImage.ptr());
System::Drawing::Bitmap^ b;
switch(colorImage.type())
{
case CV_8UC3: // non-grayscale images are correctly displayed here
b  = gcnew System::Drawing::Bitmap(colorImage.cols,colorImage.rows,colorImage.step,
System::Drawing::Imaging::PixelFormat::Format24bppRgb,ptr);
break;
case CV_8UC1: // grayscale images are incorrectly displayed here
b  = gcnew System::Drawing::Bitmap(colorImage.cols,colorImage.rows,colorImage.step,
System::Drawing::Imaging::PixelFormat::Format8bppIndexed,ptr);
break;
default:
// error message
break;
}
System::Drawing::RectangleF rect(0,0,(float)PBox->Width,(float)PBox->Height);
graphics->DrawImage(b,rect);
}


When I call this with a regular Mat captured from the webcam, it works fine. When I convert that same Mat to a grayscale, I get the weird colors. I am converting to grayscale using cvtColor(OriginalMat,OriginalMat,RGB2GRAY). The output from this does not appear to be the same channel type as the input (i.e., a CV_8UC3 going in appears to come out as a CV_8UC1). I have also forced the output to 3 channels using cvtColor(OriginalMat,OriginalMat,RGB2GRAY,3). The fact that just the colors are off makes me think that there is something with the color indexing/premultiplication but I have tried many of the the different pixelformat types and nothing seems to work. Thanks in advance for any help.

edit retag close merge delete

Before you use the grayscale you need to build a three layer image with each layer containing the grayscale info. It will work perfectly then.

( 2013-07-11 11:17:15 -0500 )edit
1

I am not sure what you mean. The OriginalMat is a CV_8UC3. So it has three channels. The output of the cvtColor should be the same type but is being converted to a CV_8UC1. As I say, when I force the output of the cvtColor to three channels (using cvtColor(OriginalMat,OriginalMat,RGB2GRAy,3) is still get the weird colors.

( 2013-07-11 15:34:49 -0500 )edit

No you have to make a mat with 3 channels and use mixchannels to clone the data into the actual channels. Just using the cvt function will screw your data up. Example of code that I use:

 Mat gray_3channels(gray.rows, gray.cols, CV_8UC3);
Mat in[] = { gray, gray, gray };
int from_to[] = { 0,0, 1,1, 2,2 };
mixChannels( in, 3, &gray_3channels, 1, from_to, 3 );

( 2013-07-11 18:56:15 -0500 )edit
1

I am probably slow, but this seems to have no effect. The sequence you are using seems no different than gray_3channels = gray.clone(). In any event, when I implement this without calling cvtColor, I just get the same color image (which is what I would expect). If I subsequently call cvtColor, I get the weird colors I had initially. It is probably just me.

( 2013-07-11 23:55:40 -0500 )edit

It is actually different. If you apply gray.clone() you will try to copy a CV_8UC1 format into a CV_8UC3 format. OpenCV will actually allow this since it stores images as long vector elements and loops through them using the specified type size. However, what my code does is taking the same grayscale values for the red, green and blue channel and mix them. This will make a CV_8UC3 from CV_8UC1 images. By selecting the same pixel values for each channel, it will produce a grayscale.

( 2013-07-12 03:02:38 -0500 )edit

Sort by » oldest newest most voted

I finally tracked this down.

Subsequent to the cvtColor call, but before my display function, I had a findContours call that I did not include in the code snippet above as I did not think it was relevant.

While it was a bit surprising, findContours alters the image it is working on. I would not have expected that but there is a note in the documentation on this.

Perhaps it makes sense to consider making this fact more prominent as this is not something that at least the lay person would suspect. Then again, maybe I am just a poor reader.

Once I ran findContours on a temporary copy of the image, everything worked correctly.

more

Can you please accept your solution then. This will indicate a solved topic!

( 2013-07-15 06:07:16 -0500 )edit
1

I can't do that. You need 50 Karma points and I only have 46. If you answer it, I will accept yours. Thanks.

( 2013-07-15 16:11:02 -0500 )edit

Basically just keep in mind that once you reach 46 karma points, you head back to previous created topics and accept where possible :) We all had to reach 50 first :)

( 2013-07-16 02:03:52 -0500 )edit

Hi, I have wrote some code like your "DrawCVImageDetect" and I have exactly the problem you said. Is it possible to write the code that solve your problem?

( 2013-07-19 06:11:44 -0500 )edit

"Once I ran findContours on a temporary copy of the image, everything worked correctly." How can he be even more explicit then this? Basically make sure you don't use same image mat element as input and output! This is because the function alters the data while processing it.

( 2013-07-19 07:01:20 -0500 )edit

Unfortunately I didn't use anything like that. I just used this code: Mat src_gray (src.size(), CV_8U); cvtColor( src, src_gray, CV_BGR2GRAY );

( 2013-07-19 07:51:33 -0500 )edit

So you are doing exactly the same. Instead of using two times the src matrix, you have made a new matrix src_gray, which is equal size of src matrix.... like he said so.

( 2013-07-19 08:15:23 -0500 )edit

Official site

GitHub

Wiki

Documentation