How can I get the pixel data bytes from a Xamarin.IOS UIImage object or from a Xamarin.IOS UIImageView.Image ?
I want to get the pixel data bytes so I can write them to a file, and later use LoadFromData() to load the UIImage.
You can use this.
using (NSData imageData = image.AsPNG()) {
Byte[] myByteArray = new Byte[imageData.Length];
System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, myByteArray, 0, Convert.ToInt32(imageData.Length));
}
Source: Converting UIImage to Byte Array
Related
I want to convert the ImageSource or image byte array to Xamarin.ios NSUrl or CGImage. How to implement this. I am generating the byte Array from LayoutView like below. So after getting the the byte array I need to convert it to NSUrl or CGImage for printing that layout. From the below code I will get the bytearray.
public byte[] LayoutToImage()
{
var view = UIApplication.SharedApplication.KeyWindow;
UIGraphics.BeginImageContext(view.Frame.Size);
view.DrawViewHierarchy(view.Frame, true);
var image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
using (var imageData = image.AsPNG())
{
var bytes = new byte[imageData.Length];
System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, bytes, 0, Convert.ToInt32(imageData.Length));
return bytes;
}
}
I am able to print the image on the iOS project resource file, like that I need to print the layout view also.
string fileName = "SampleImg.png";
string localDocUrl = Path.Combine(NSBundle.MainBundle.BundlePath, fileName);var Error = Driver.PrintImageWithURL(new NSUrl(localDocUrl), pSettings);
As Jason said GetImageFromCurrentImageContext returns a UIImage. If you want to convert to CGImage, you can directly use UIImage.CGImage to get CGImage. Converting a byte array to a CGImage also needs to be converted to a UIImage first.
I am trying to read the bytes of a .png image using File.ReadAllBytes(string) method without success.
My images are of size 2464x2056x3 (15.197.952 bytes), but this method returns an array of about 12.000.000 bytes.
I tried with a white image of the same size, and I am getting a byte array of 25.549, and checking the byte array I can see all kind of values, that obviously is not correct because is a white image.
The code I am using is:
var frame = File.ReadAllBytes("C:\\workspace\\white.png");
I've also tried to open the image first as an Image object then get the byte array with the following:
using (var ms = new MemoryStream())
{
var imageIn = Image.FromFile("C:\\workspace\\white.png");
imageIn.Save(ms, imageIn.RawFormat);
var array = ms.ToArray();
}
But the result is the same as before...
Any idea of what's happening?
How can I read the byte array?
PNG is a compressed format.
See some info about it: Portable Network Graphics - Wikipedia.
This means that the binary representation is not the actual pixel values that you expect.
You need some kind of a PNG decoder to get the pixel values from the compressed data.
This post might steer you in the right direction: Reading a PNG image file in .Net 2.0. Note that it's quite old, maybe there are newer methods for doing it.
On a side note: even a non compressed format like BMP has a header, so you can't simply read the binary file and get the pixel values in a trivial way.
Update:
One way to get the pixel values from a PNG file is demonstrated below:
using System.Drawing.Imaging;
byte[] GetPngPixels(string filename)
{
byte[] rgbValues = null;
// Load the png and convert to Bitmap. This will use a .NET PNG decoder:
using (var imageIn = Image.FromFile(filename))
using (var bmp = new Bitmap(imageIn))
{
// Lock the pixel data to gain low level access:
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Unlock the pixel data:
bmp.UnlockBits(bmpData);
}
// Here rgbValues is an array of the pixel values.
return rgbValues;
}
This method will return a byte array with the size that you expect.
In order to use the data with opencv (or any similar usage), I advise you to enhance my code example and return also the image metadata (width, height, stride, pixel-format). You will need this metadata to construct a cv::Mat.
I have the Image ByteArray and want to convert the byte array into png image and add in ImageView as you see in the below code.
byte[] imageBytes = webClient.DownloadDataTaskAsync(uri);
ImageView view = new ImageView(this.Context);
//Here need to add the converted image into ImageView
view.SetImageSource();
I achieved this, by converting ImageBytes into a bitmap and add the bitmap in ImageView. But it has some memory problem. As I adding more no.of times frequently in my source, I couldn't use a bitmap to add in ImageView due to the memory exception.
So please help me.
Thanks.
You can do it by creating the bitmap from Stream to do that use this:
using(var ms = new MemoryStream(imageBytes))
{
var bitmap = BitmapFactory.DecodeStream(ms);
// ...
// rest of your logic here...
// ...
}
Hope this helps
It should be as easy as calling
var bitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
Android.Graphics.BitmapFactory.DecodeByteArray Method
Decode an immutable bitmap from the specified byte array.
Parameters
data
byte array of compressed image data
offset
offset into imageData for where the decoder should begin parsing.
length
the number of bytes, beginning at offset, to parse
opts
null-ok; Options that control downsampling and whether the image should be completely decoded, or just is size returned.
I would need to take a screenshot, if easy to without saving it. I would sent the image data directly to a PHP script. Because I don't have this PHP script at the moment, so I search for the easiest way of format in which I should convert the screenshot data. For debugging reasons until I've got my PHP script I would like to convert a picture of these data on my client side using C#.
My code at the moment for taking a screenshot and convert it (I'm not sure if I can convert the output in my logfile back into a picture):
internal static byte[] ImageToByteArray(Image img)
{
byte[] byteArray = new byte[0];
MemoryStream stream = new MemoryStream();
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
stream.Close();
byteArray = stream.ToArray();
return byteArray;
}
public static string TakeScreenshot()
{
String filepath = #"C:\log2.txt";
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
graphics.Dispose();
bitmap.Save("C:\\temp.png");
Image img = (Image)bitmap;
string str = System.Text.Encoding.Default.GetString(ImageToByteArray(img));
System.IO.File.AppendAllText(filepath, str);
//just for debugging
return "OH";
}
Main question at the moment, is there any way to get a picture back from my converted code (log2.txt).
This line of code:
string str = System.Text.Encoding.Default.GetString(ImageToByteArray(img));
Will almost certainly not do what you want it to do. It will try to interpret your byte array as a string in whatever is the default character encoding on your machine. If the default is something like UTF-8 or any multibyte character set, then it's quite likely to fail. Even if the default encoding is a single-byte character set, it could create a string that can't be reliably turned back into the original byte array.
If you really want to store the byte array as text, you can call Convert.ToBase64String:
string str = Convert.ToBase64String(ImageToByteArray(img));
If you read that string back from the file into str, you can rebuild the byte array with:
byte[] imageBytes = Convert.FromBase64String(str);
Another advantage in your particular case is that there are PHP functions for dealing with base 64 strings.
I need to get the raw bitmap data only (no header, or other information). I used the following code to get the bitmap data:
using (Bitmap bitmap = svgDocument.Draw())
{
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
var length = Math.Abs(bitmapData.Stride) * bitmapData.Height;
byte[] bytes = new byte[length];
Marshal.Copy(bitmapData.Scan0, bytes, 0, length);
bitmap.UnlockBits(bitmapData);
MemoryStream memoryStream = new MemoryStream();
string filename = DateTime.Now.Ticks.ToString() + ".bmp"; // this works fine
bitmap.Save(filename, ImageFormat.Bmp);
string base64 = Convert.ToBase64String(bytes, Base64FormattingOptions.InsertLineBreaks); // the base64 is reversed.
}
When I save the bitmap, everything looks fine. The image is not reversed. However when I use the bytes only to convert the data to Base64, then the image is reversed.
Edit 1:
I think this has nothing to do with the Base64 conversion. It seems that the bytes are in reversed order.
When I save the image using the code, the image looks like this:
When I use the bytes, then I see this:
Solution:
I found the solution. Instead of creating a new bitmap, I just skipped the first 54 bytes of header information and then stored the byte array.
MemoryStream memoryStream = new MemoryStream();
bitmap.Save(memoryStream, ImageFormat.Bmp);
// Skip header
IEnumerable<byte> bytes = memoryStream.ToArray().Skip(54);
The standard BMP format allows to store bytes of the image either in classical top/down order or in reverse order.
The way to tell whether your image is stored this way is to check the value of the Height parameter in the BMP header:
If Height < 0, then the height of your image is abs(Height) and the pixels are stored in reverse (bottom / top) order.
If Height > 0, then you are in the case you expect, where pixels are in 'normal' order, top to bottom.
I would say that what happens in your case is that you are starting from an image stored with a negative Height header (the SVG object must do that for some reason). But you do not check it, so you store the pixels in bottom to top order.
When you store with the BMP object, it figures that out for you from the context. But when you export just the pixels, then the third party application that loads your image sees positive Height and thus shows your image upside down.
You can find details about this pixel ordering in the Wikipedia page for BMP file format.
Edit:
So, when you write a BMP file to your disk, you have to do the following:
Check whether your bytes are in top to bottom order (a) or in bottom to top order (b)
If (a): put the height of your image as a positive value in the BMP header
If (b): put - height as a negative value in the BMP header. So that the third party program that shows your image knows that it's reversed.
I found the solution. Instead of creating a new bitmap, I just skipped the first 54 bytes of header information and then stored the byte array.
MemoryStream memoryStream = new MemoryStream();
bitmap.Save(memoryStream, ImageFormat.Bmp);
// Skip header
IEnumerable<byte> bytes = memoryStream.ToArray().Skip(54);
I ran into the same problem but still needed a base 64 string (for HTML5 canvas). So I used the Image class to rotate the image, stored in a new stream, then convert to base64 string.
var image = System.Drawing.Image.FromStream(new MemoryStream(bytes));
//Rotate and save to new stream
image.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);
MemoryStream streamOut = new MemoryStream();
image.Save(streamOut, System.Drawing.Imaging.ImageFormat.Jpeg);
//Convert to base64 string
string base64String = Convert.ToBase64String(streamOut.ToArray());