I'm trying to produce a system which takes an image in OpenCV, writes it to XML, reads it from XML, and displays it in an Image control in a WPF application.
The problem I'm having is that I'm unclear as to how .NET classes (e.g. BitmapImage) process images from a byte array. I've tried a number of methods and gotten various errors - too numerous to post unless required.
I was wondering if someone could point me in the right direction regarding this? What I want to know is how does the data need to be presented to a .NET object in order to display it in an Image control?
Thanks in advance for your help - it is much appreciated.
EDIT: I expect what I need to do is convert the XML into a byte array and use that, along with a definition of the format, to instantiate a BitmapImage object or equivalent to use as source for the Image control. What I'm not sure of is how to do this.
Use int cvSaveImage(const char* filename, const CvArr* image)
to save the file as an Image.The image format is chosen based on the filename extension
No need to store it in XML if you can directly store it as Image file.
The .NET classes requires an Image.That's it.
Note:
If you want to store red,green,blue values as comma separated strings, it would be very inefficient.Save it in Base64 instead.You can then convert it into bytes and feed it to any .NET image class.Also the .NET ImageConverter Class may be very helpful.
I found that the issue was (presumably) that I wasn't Base64 encoding the XML data before writing it. Thus the solution is to Base64 encode the data before writing to XML, Base64 decoding it on the other side, reading the data as a byte array and using that to instantiate a MemoryStream and then a BitmapImage object.
Thanks for your help everyone.
You can use:
public static BitmapSource Create(
int pixelWidth,
int pixelHeight,
double dpiX,
double dpiY,
PixelFormat pixelFormat,
BitmapPalette palette,
Array pixels,
int stride
)
Related
I try to make an application in unity, using OpenCV. I need the Absdiff from two pictures.
My Problem is that I get an OpenCvSharp.OutputArray from the Cv2.Absdiff(processImageTwo, processImageOne, differenceImage); function and I cant't convert it to a byte[], which I'll need to store the image.
Can somebody help me, how to convert an OpenCvSharp.OutputArray to byte?
Please try:
byte[] bytes = differenceImage.GetMat().ToBytes();
This should convert the image to png byte format.
See https://shimat.github.io/opencvsharp_docs
I am trying to convert YUV420 frames to Bitmap or Image. I am reading these frames from an MP4 video in C# using the AVBlocks library. So, after creating an input and output socket using AVBlocks classes, I then pull each frame from the video with a YUV420 color format and UncompressedVideo stream type. I basically do this by calling Transcoder.Pull(int outputIndex, MediaSample outputData) and then the MediaBuffer that's part of the outputData has the data in an array of bytes. So I am trying to convert these bytes to a Bitmap or Image so that I can eventually show each frame into a PictureBox in the Winforms application.
What I've tried:
I have tried using a MemoryStream, as shown below, but I get an unhandled ArgumentException saying that the parameter is not valid. I tried using ImageConverter() as well to convert to an Image, but I get the same exception. Then, I converted the byte array from YUV to RGB format and gave the updated array as a parameter to the MemoryStream, but again no luck. I also tried changing the color format of the output socket from YUV420 to a BGR format, but it resulted in the same issue as above. The code that tries to convert to a bitmap using MemoryStream:
while (transcoder.Pull(out inputIndex, yuvFrame))
{
buffer = (MediaBuffer) yuvFrame.Buffer.Clone();
Bitmap b;
byte[] temp = new byte[buffer.DataSize];
Array.Copy(buffer.Start, buffer.DataOffset, temp, 0, buffer.DataSize);
var ms = new MemoryStream(temp);
b = new Bitmap(ms);
}
The aforementioned exception is thrown in the last line of the code. I'm not sure if it's the color format or the stream type, or something else that's causing the problem. If someone wants to see more of the code (setting up input & output sockets etc), let me know. For reference, the link to the example I've been following from AVBlocks is this and the link to MediaBuffer class is this.
The Bitmap(MemoryStream ms) constructor expects the bytes from an actual file, like a png, jpeg, bmp or gif. If I'm reading this correctly, you don't have that; you only have pure RGB triplets data. That isn't enough, because it lacks all information about the image's width, height, colour depth etc.
You will need to actually construct an image object from the RGB data. This isn't really trivial; it means you need to make a new image object with the correct dimensions and colour format, then access its backing bytes array, and write your data into it. The actual code for creating an image out of a byte array can be found in this answer.
Note that you'll have to take into account the actual stride in the resulting data you get; the amount of bytes on each line of the image. Images are saved per line, and those lines are usually padded to a multiple of 4 bytes. This obviously messes up a lot if you don't take it into account.
If your data is completely compact, then the stride to give to the BuildImage function I linked to will just be your image width multiplied by the amount of bytes per pixel (should be 3 for 24bpp RGB), but if not, you'll have to pad it to the next multiple of 4.
I need to convert an picture (that is stored in an object of type Image) to a string for storage (and later for conversion back into an Image object for display) in a metro app
I have found lots of answers for converting an image to a base64 string in .NET 4.0 etc but in 4.5 the System.Windows.Bitmap namespace isn't there (the Image class is in Windows.UI.Xaml.Media.Imaging) and the method that was in that namespace that made it possible in 4.0 "Save()" doesn't seem to be in 4.5...unless I just can't find it.
Theres an example of doing this here but like I said it doesn't work in a metro app/.NET 4.5
any ideas?
more details:
the method that will do this will convert an instance field that contains an image object (ive used its source property, is this correct?) and needs to store the resultant string from the conversion in an instance string field. this whole object can then be serialized, ignoring the Image field, with the hope of deserializing later and restoring the string to the Image field for display. so far ive tried to use a DataContractSerializer to serialize string from the image, but it doesn't seem to like it. Once I get a string from the image I would be able to serialize that, but its not something I've ever done before.
Also, it seems that the only .net 4.5 documentation that is definitely correct is the pages here: http://msdn.microsoft.com/library/windows/apps/
pages at the "normal looking" msdn site for .net 4.5 don't seem to always work in metro apps? (just a theory?)
[solved]
I finally got it! for anyone else that ever has to do this the answer is here: http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/38c6cb85-7454-424f-ae94-32782c036567/
I did this
var reader = new DataReader(myMemoryStream.GetInputStreamAt(0));
var bytes = new byte[myMemoryStream.Size];
await reader.LoadAsync((uint)myMemoryStream.Size);
reader.ReadBytes(bytes);
after this sequence, the byte array bytes will have the data from the stream in it, from there I set a string to the value of
Convert.ToBase64String(bytes);
I finally got it! for anyone else that ever has to do this the answer is here: http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/38c6cb85-7454-424f-ae94-32782c036567/
I did this
var reader = new DataReader(myMemoryStream.GetInputStreamAt(0));
var bytes = new byte[myMemoryStream.Size];
await reader.LoadAsync((uint)myMemoryStream.Size);
reader.ReadBytes(bytes);
after this sequence, the byte array bytes will have the data from the stream in it, from there I set a string to the value of
Convert.ToBase64String(bytes);
I'm not sure of this because I do not have the .net 4.5 installed here, but I think this could work:
You could use the BitmapSource.CopyPixels() method to extract the pixels of the image:
http://msdn.microsoft.com/en-us/library/ms616043(v=vs.110).aspx
Then use Convert.ToBase64String() to do the convertion.
Also, here are some useful imaging HOW-TOs:
http://msdn.microsoft.com/en-us/library/ms750864(v=vs.110)
Try BitmapEncoder. Example of how to create a BitmapEncoder here. The appropriate namespace is Windows.Graphics.Imaging.
BitmapEncoder gets you an encoder. You can then use GetPixelDataAsync(BitmapPixelFormat, BitmapAlphaMode, BitmapTransform, ExifOrientationMode, ColorManagementMode) to get your pixel data. Following that you can use any generic C# base64 encoder.
(Examples are Javascript but should work for C# as well since the classes exist in C#)
You should store the encoded format of image (say JPEG) format, decoded back to byte[], create a MemoryStream, then Metro BitmapImage can be created from the stream.
Here is the situation:
I have got the array of bytes containing the bitmap header together with the bitmap data. I know the offset of data, and I need to generate BitmapFrame for later rendering.
I wanted to avoid array cells copying and created a wrapper for a part of the array
(
ArraySegment ).
Then I render this bitmap together with a primitive into the RenderTargetBitmap, which is, in turn, saved to the same array (in place of data) using CopyPixels.
I have noticed, that if I work on the original data, with an array wrapper I get the wrong final image. However if I copy the same data to another array and provide it to static Create method of BitmapFrame class, the result is correct.
Do you have any ideas what happens?
Thank you in advance for the replies!
Cheers
OK, I have identified the problem - a property of ArraySegment returns the original array - no the subarray...
I'm loading a Bitmap from a jpg file. If the image is not 24bit RGB, I'd like to convert it. The conversion should be fairly fast. The images I'm loading are up to huge (9000*9000 pixel with a compressed size of 40-50MB). How can this be done?
Btw: I don't want to use any external libraries if possible. But if you know of an open source utility class performing the most common imaging tasks, I'd be happy to hear about it. Thanks in advance.
The jpeg should start with 0xFF 0xD8. After that you will find various fields in the format:
Field identifier 2 bytes
Field length, excluding field identifier. 2 bytes.
Variable data.
Parse through the fields. The identifier you will be looking for is 0xFF 0xC0. This is called SOF0, and contains height, width, bit depth, etc. 0xFF 0xC0 will be followed by two bytes for the field length. Immediately following that will be a single byte showing the bit depth, which will usually be 8. Then there will be two bytes for height, two for width, and a single byte for the number of components; this will usually be 1 (for greyscale) or 3. (for color)
This isn't something I've tried myself, but I think you might need to acccess the picture's EXIF information as a start.
Check out Scott Hanselman's blog-entry on accessing EXIF information from pictures.
Standard .NET System.Drawing namespace should have all that you need,
but it probably won't be very efficient. It'll load the whole thing into RAM, uncompress it, convert it (probably by making a copy) and then re-compress and save it. If you aim for high performance, I'm afraid you might need to look into C/C++ libraries and make .NET wrappers for them.
As far as I know jpg is always 24 bpp. The only thing that could change would be that it's CMY(K?) rather then RGB. That information would be stored in the header. Unfortunately I don't have any means of creating a CMYK image to test whether loading into a Bitmap will convert it automatically.
The following line will read the file into memory:
Bitmap image = Image.FromFile(fileName);
image.PixelFormat will tell you the image format. However, I can't test what the file load does with files other than 24bpp RGB jpgs. I can only recommend that you try it out.