.NET Forum / Languages / C# / January 2008
Image has wrong colors
|
|
Thread rating:  |
Joe Thompson - 18 Jan 2008 04:56 GMT I have a Windows form program written in C# 2005. I continually read a stream of data via TCP that comes from a video camera (NTSC). When I have all the data for one frame I update a picturebox (pbVideo) like this:
pbVideo.Image = (Bitmap)Bitmap.FromStream(new MemoryStream(m_ImageBuff, 0, m_TotalImageBytes), true, true); or this pbVideo.Image = (Bitmap)Bitmap.FromStream(new MemoryStream(m_ImageBuff, 0, m_TotalImageBytes));
Everything works as expected except the colors are wrong. For example, the things that should look yellow are turquoise and the things that should be blue appear as red or orange.
The data stream is in mjpeg format - I just pick out the frames one at a time. Any ideas or suggestions would be appreciated...
Thank you, Joe
Michael C - 18 Jan 2008 05:12 GMT >I have a Windows form program written in C# 2005. > I continually read a stream of data via TCP that comes from a video camera [quoted text clipped - 17 lines] > time. > Any ideas or suggestions would be appreciated... What is mjpeg format? Possibly you should be using Bitmap.LockBits and copying the data in as it is received. That way you keep only 1 copy of the image data and can display it as it comes in.
Michael
Joe Thompson - 18 Jan 2008 05:44 GMT Hi Michael,
Thank you for the reply. I'm not sure of the format - I beleive it's 24 bit color. Each frame is just a jpg file. I am trying to rewrite an application that was written on a Linux box and it displays the colors correctly (I don't have that code).
As far as my app goes, I gather the bytes 1450 at a time until I get a full frame. Then I build the whole bitmap at once. I'm not familiar with LockBits - what does it do? I was hoping it was just a color map problem or something like that.
Thank you, Joe
Peter Duniho - 18 Jan 2008 05:59 GMT > Thank you for the reply. I'm not sure of the format - I beleive it's 24 > bit [quoted text clipped - 3 lines] > don't > have that code). Do you know whether the Linux code is using a plain JPEG library, or has something custom-written for the purpose?
I would think that if the former, the code you're using should work. But if the latter, all bets are off.
I don't know enough about the M-JPEG format to comment with specifics. However, I do know that many video formats are _not_ RGB, and it's possible that the same is true for the data you're receiving.
To be honest, your question doesn't sound like the sort of thing that's likely to get a good answer in this newsgroup. You should monitor the thread just in case, but I would look to a forum more specific to video streaming, perhaps even specific to the device you're using (there is as far as I know no well-defined specification for M-JPEG, so you may need device-specific advice).
Pete
Joe Thompson - 18 Jan 2008 07:21 GMT Hi Peter,
I'm really not sure the format it is using - I may be able to find out Monday. Hopefully this thread isn't buried too deep by then. I was originally going to post this in the Graphics and Multimedia group but it seems more of a C# issue - maybe I will though.
Thanks for the help, Joe
Michael C - 18 Jan 2008 07:39 GMT > Hi Peter, > > I'm really not sure the format it is using - I may be able to find out > Monday. Hopefully this thread isn't buried too deep by then. I was > originally going to post this in the Graphics and Multimedia group but it > seems more of a C# issue - maybe I will though. This would be your best bet I would think as these sort of issues are dealt with all the time there: microsoft.public.dotnet.framework.drawing
Michael
Michael C - 18 Jan 2008 06:24 GMT > Hi Michael, > [quoted text clipped - 5 lines] > don't > have that code). Can you save the data to a file and open it using an image program? Does it look the same as what you get in C#?
> As far as my app goes, I gather the bytes 1450 at a time until I get a > full > frame. Then I build the whole bitmap at once. I'm not familiar with > LockBits - what does it do? I was hoping it was just a color map problem > or > something like that. LockBits gives you high speed access to the raw data in a bitmap object. It's possible if it's coming across from a linux box that it's a little/big endian issue and the RGB values are just back to front. If the image is correct except that the colours are wrong then this is what you could do: 1) Create the bitmap as you are doing currently. 2) Use LockBits to 'fix' all the data in the bitmap.
Lockbits is slightly more challenging than most C# card but it's nothing too difficult.
Try attaching the bitmap to a post, I know that's frowned apon here but we can cope with 1450 bytes.
Michael
Peter Duniho - 18 Jan 2008 06:57 GMT > Can you save the data to a file and open it using an image program? Does > it > look the same as what you get in C#? I suspect that if he saves the buffer out to a file, he'll get the same results. But I agree that would be a good test. It would at least confirm that it's the data itself that's not being interpreted correctly, as opposed to some other problem.
> LockBits gives you high speed access to the raw data in a bitmap object. > It's possible if it's coming across from a linux box that it's a > little/big > endian issue and the RGB values are just back to front. That's not a bad guess, but I would be surprised if it's the actual issue. Byte order for JPEG data is well-defined regardless of platform (big-endian) so no JPEG-aware code should be messing with the order. So unless at some point something that's not JPEG-aware is reading the byte stream and swapping the bytes explicitly, the byte ordering within the stream shouldn't change. And even if that did happen, more than just the RGB values would be messed up.
> Try attaching the bitmap to a post, I know that's frowned apon here but > we > can cope with 1450 bytes. Well, a) many ISPs will block _any_ attachment, no matter how small, and b) he's reading data 1450 at a time, but the bitmaps themselves are larger than that. He doesn't say how large a full frame is, and it will vary from frame to frame never mind according to the frame resolution and rate, but I'd guess that even at a lower resolution like 320x240 a single frame would be 20K or so.
Much better, if it's useful to share the data at all, is to put it on a website for download. Here are links for three of the many free websites that offer upload/download services:
http://www.filecrunch.com/ http://www.sendspace.com/ http://www.yousendit.com/
Of course, many people have some sort of personal web site or similar where they can put files as well. In any case, copying the file to some place like that is much better than having the data inflated some 30% or more by the MIME encoding, and then copied everywhere in the world, to every server carrying the newsgroup and to every user downloading messages, even though a handful of people at most will ever actually look at it.
:) Pete
Joe Thompson - 18 Jan 2008 07:12 GMT Hi Michael,
Yes, I can and do log it to a file also. When I display it in Paint or just use "Preview" from windows it still looks messed up. The file is actually larger than 1450 bytes, that's just how many I read at a time. The picture is at work and I'm off until Monday.
What I meant to say is we already have a Linux laptop with an application that receives the same data but displays is correctly. The app I'm writing in windows doesn't. Actually, when the camera is set to black and white, the image looks good. Just the color mode produces a bad picture. Would this be the case if it was a big/little endian issue? Is there a way I could apply a PixelFormat to the jpg to correct it?
Thanks for all your help, Joe
Michael C - 18 Jan 2008 07:31 GMT > Hi Michael, > [quoted text clipped - 9 lines] > writing > in windows doesn't. That is interesting.
> Actually, when the camera is set to black and white, the > image looks good. Just the color mode produces a bad picture. Would this > be > the case if it was a big/little endian issue? Yes (although big endian might not be technically the correct term). It's likely windows is expecting RGB when the webcam is delivering BGR.
> Is there a way I could apply > a PixelFormat to the jpg to correct it? You could fix it with a ColorMatrix which is fairly easy to do. You'd define a matrix like this I think:
0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1
Try this air code:
ColorMatrix cm = new ColorMatrix( above values in an array); Graphics g = Graphics.FromImage(myBitmap); ImageAttributes ia = new imageAttributes(); ia.SetColorMatrix(cm); g.DrawImage(myBitmap,,,,,ia); //<- fill in the rest of the params g.dispose();
You can achieve the same thing using LockBits probably faster but with more code and it needs to be unsafe code. I would probably use LockBits myself although the above will work quite well also. I'm presuming you can draw the bitmap onto itself, if not you might need to create a second bitmap. I've not tested the above code but I think it should work. If you want a lockbits example let me know. I won't be back till monday either. Have a good weekend.
Regards, Michael
Joe Thompson - 18 Jan 2008 16:52 GMT Hi Michael,
I'll definitely give that a try by Monday and let you know.
Thanks again, Joe
Joe Thompson - 21 Jan 2008 19:01 GMT Hi Michael,
I was able to implement and run my program using the LockBits approach. It seems to keep up pretty well at 15 frames/sec but a little worse at 30. If I drop the resolution from 320 x 240 to 160 x 120 it gets better.
I would still like to try it using the ColorMatrix approach. The little test app I ran at home was much simpler - I can't seem to figure out what to do in my real app.
I have a picturebox named pbVideo, and a byte[] named m_ImageBuff. All I had to do was one line of code everytime I had a complete frame:
pbVideo.Image = (Bitmap)Bitmap.FromStream(new MemoryStream(m_ImageBuff, 0, m_TotalImageBytes));
Now I'm confused in the ColorMatrix approach - after I do g.DrawImage how do I get that image to my picturebox? pbVideo.Image = ???
Thanks again, Joe
All
Michael C - 21 Jan 2008 21:50 GMT > Hi Michael, > [quoted text clipped - 18 lines] > do > I get that image to my picturebox? pbVideo.Image = ??? Just something like this: Bitmap myBitmap = (Bitmap)Bitmap.FromStream(new MemoryStream(m_ImageBuff, 0, m_TotalImageBytes)); ..... g.DrawImage(myBitmap....); pbVideo.Image = myBitmap;
But I would abandon the picturebox myself and just do this: Graphics g = this.CreateGraphics();//this being a form //insert code here to create color matrix g.drawImage(myBitmap, .... ) g.Dispose();
That way you're cutting down on CPU usage a lot because you're cutting out 1 step by drawing straight to the form. I spent 4 years writing an imaging app and only used 1 picturebox which was to display an image in the About screen.
> Thanks again, > Joe > > All Joe Thompson - 18 Jan 2008 17:08 GMT Michael,
Great news - I just had a guy at work email me a bad jpg , applyied the colormatrix and it worked! Monday I'll try it "real time" to see if I can keep up with the fastest video rate (I think it's 20 x second). Any suggestions on this are welcome.
Thanks again for all your help, Joe
Michael C - 19 Jan 2008 04:51 GMT > Michael, > > Great news - I just had a guy at work email me a bad jpg , applyied the > colormatrix and it worked! Monday I'll try it "real time" to see if I can > keep up with the fastest video rate (I think it's 20 x second). Any > suggestions on this are welcome. Yep, try this code also, I'd be suprised if it couldn't keep up with 20 frames per sec. You'll need to mark it as unsafe and allow unsafe code in your assembly. Out of interest, if you compare the 2 methods can you post the max framerate of each?
Bitmap bitmap = GetBitmapFromTCP(....); int width = bitmap.Width; int height = bitmap.Height; BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int offset = data.Stride - data.Width * 3; byte* ptr = (byte*)data.Scan0; for(int y = 0; y < height; y++, ptr += offset) { for(int x = 0; x < width; x++, ptr += 3) { byte swap = ptr[0]; ptr[0] = ptr[2]; ptr[2] = swap; } } bitmap.LockBits(data);
Michael
Joe Thompson - 23 Jan 2008 00:22 GMT Hi Michael,
I have both methods working now:
// ColorMatrix approach - works! // m_bmpPicture = (Bitmap)Bitmap.FromStream(new MemoryStream(m_ImageBuff, 0, m_TotalImageBytes));
if (m_bmpPicture.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb) { m_gfxPicture.DrawImage(m_bmpPicture, m_rctPicture, 0, 0, m_bmpPicture.Width, m_bmpPicture.Height, GraphicsUnit.Pixel, m_iaPicture); } else { pbVideo.Image = m_bmpPicture; }
Or...
// LockBits approach - works! // m_bmpPicture = (Bitmap)Bitmap.FromStream(new MemoryStream(m_ImageBuff, 0, m_TotalImageBytes));
if (m_bmpPicture.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb) { ConvertToRGB(ref m_bmpPicture); } pbVideo.Image = m_bmpPicture;
public static bool ConvertToRGB(ref Bitmap b) { int width = b.Width; int height = b.Height; BitmapData data = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int offset = data.Stride - data.Width * 3;
unsafe { byte* ptr = (byte*)data.Scan0; for (int y = 0; y < height; y++, ptr += offset) { for (int x = 0; x < width; x++, ptr += 3) { byte swap = ptr[0]; ptr[0] = ptr[2]; ptr[2] = swap; } } } b.UnlockBits(data); return true;
}
Both seem to run about the same speed but I haven't actually measured them in any way. My biggest problem now is I need to build the inital bitmap faster. I'm collecting the video data on one port and other telemetry data on another port.
Thanks again for all your help, Joe
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|