I am trying to save a byte array (of an image) to the file system using C# and it isn't working using either method I have tried, here is code sample 1:
string path = #"c:\ppd" + g.ToString() + ".jpg";
using (MemoryStream inputStream = new MemoryStream(image))
{
Image returnImage = Image.FromStream(inputStream);
returnImage.Save(path, ImageFormat.Jpeg);
}
With this code I get 'Parameter is not valid' when sending the data to the service. So that's obviously the wrong code for the array, so I hit google and came up with this code:
string path = #"c:\ppd\" + g.ToString() + ".jpg";
using (MemoryStream inputStream = new MemoryStream(image))
{
using (Stream file = File.Create(path))
{
byte[] buffer = new byte[8 * 1024];
int len;
while ((len = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
file.Write(buffer, 0, len);
}
}
}
This code writes the file to the file system, but when you try to open it it helpfully says 'This is not a valid bitmap file, or it's format is not currently supported.
We get the byteArray from Flex using:
bitmapData.getPixels(0, 0, bitmapData.width, bitmapData.height);
And the bitmapData comes from the Android camera (via the Distriqt Camera ANE). The image displays fine in the app, but I have no idea where to go next with this, please help.
EDIT:
The code in Flex to get the byteArray is:
<s:BitmapImage id="bitmapImage" width="100%"
maxHeight="{this.height/2}"
source="{profileModel.captureBitmapPreview}"
/>
<s:Button click="{dispatchEvent(new PictureEvent
(PictureEvent.UPLOAD,
0, pictureModel.captureBitmapPreview.getPixels(new Rectangle(0,0,pictureModel.captureBitmapPreview.width,pictureModel.captureBitmapPreview.height))))}"/>
/* this is the alternative which still doesn't work **/
<s:Button click="{dispatchEvent(new PictureEvent
(PictureEvent.UPLOAD,
0, bitmapImage.getPixels(new Rectangle(0,0,bitmapImage.width,bitmapImage.height))))}"/>
PictureEvent takes 3 parameters, type:String, id:int = 0, image:ByteArray = null.
SECOND EDIT: Here is the code that creates the bitmapData:
private function camera_capturedImageHandler(evt:CameraDataEvent):void
{
Camera.instance.setPresetMode(CameraMode.PRESET_MEDIUM);
Camera.instance.addEventListener(CameraEvent.VIDEO_FRAME, camera_videoFrameHandler, false, 0, true);
if (_captureBitmapData.width != evt.data.width || _captureBitmapData.height != evt.data.height)
{
_captureBitmapData = new BitmapData(evt.data.width, evt.data.height, false);
}
_captureBitmapData.draw(evt.data);
var tbd:BitmapData = new BitmapData(FlexGlobals.topLevelApplication.width, FlexGlobals.topLevelApplication.height, false)
tbd = applyOrientation(_bitmapData, "6");
profileModel.captureBitmapPreview.copyPixels(tbd, new Rectangle(0, ((FlexGlobals.topLevelApplication.height/2)-(FlexGlobals.topLevelApplication.height/4)), FlexGlobals.topLevelApplication.width, FlexGlobals.topLevelApplication.height/2), new Point(0, 0));
}
THIRD EDIT:
I has done some testing and discovered that this code should work, it is a variation on the first block above that throws 'Parameter is not valid'
ImageConverter imageConverter = new ImageConverter();
Bitmap bm = (Bitmap)imageConverter.ConvertFrom(image);
if (bm != null && (bm.HorizontalResolution != (int)bm.HorizontalResolution || bm.VerticalResolution != (int)bm.VerticalResolution))
{
bm.SetResolution((int)(bm.HorizontalResolution + 0.5f),(int)(bm.VerticalResolution + 0.5f));
}
bm.Save(path, ImageFormat.Jpeg);
So now I am sure the error is with the byteArray, but I genuinely cannot see what I am doing wrong. I have the bitmapData and it is valid, and it looks like I am collecting the data I need, the written file is nearly 700Kb in size, but something is wrong.
If someone could tell me what is wrong on the Flex side of this equation I would be forever grateful. I have the BitmapImage on the display list, it is bound to the model correctly and I have tried different ways of pointing to the source and drawing up the rectangle.
Ok, so #Lorek was right, the byteArray just contains pixel data and no formatting information. We don't know why this is the case but based on that knowledge and a little digging in the database I realised that this has been the situation for a while and I needed a solution that would solve it.
So in answer to the question 'How do you convert pixel data from a byteArray into an image?' I came up with this, still need a little help, but basically this solves the issue I am having (more after the code)
Bitmap bitmap = new Bitmap(150, 150, PixelFormat.Format32bppRgb);
BitmapData bmData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(image, 0, pNative, image.Length);
bitmap.UnlockBits(bmData);
bitmap.Save(path, ImageFormat.Png);
So I change the Rectangle size in Flex so I knew what I was working with, and posted the btyeArray to this method, which beautifully writes out a PNG for me.
It's slightly blue, well it's really blue and I am not quite sure why, take a look:
http://ec2-52-89-85-67.us-west-2.compute.amazonaws.com/imagedata/ppd/3898ae89-e4e0-4d03-97d7-dac4e3b618d5.png
It is supposed to be black:
http://ec2-52-89-85-67.us-west-2.compute.amazonaws.com/imagedata/ppd/capture.png
So any of you .Net people know what happened and can advise me (I heard somewhere that I need to switch the order of the colors so instead of rgb it gets switched around, but I have no idea how or why this would be the issue).
EDIT Here is the solution to that, it's slow, it's pointless and it takes too long: http://www.codeproject.com/Articles/2056/Image-Processing-for-Dummies-with-C-and-GDI-Part-3
Or if maybe we can solve the actual Flex issue that's be great although I have given up all hope of SO being useful for Flex support any more
Thanks you everyone for your patience.
Related
What I want to do:
Given a simple Bitmap (System.Drawing.Bitmap) I am willing to output it to my window in WPF application. I am also willing to do it frequently, creating a frame stream.
What I've used:
Firstly, I've been converting Bitmap to a BitmapImage and then assigning it to Source field of an Image control.
The problem with this method is the conversion itself. It's very slow. I haven't found a way that works fast enough, the best one took around 20 ms for a 640x480 Bitmap, which is slow. I am hoping to find a way that would do this for less than 5 ms for any common resolution or a different approach to the whole problem. Perhaps, there is a different control other than Image that would work with pure Bitmap and I wouldn't need to convert. I am also not tied to using WPF, does UWP or the new WinUI 3 have this problem?
I have checked UWP uses WriteableBitmap, which would also require conversion, but maybe there is a different way as well?
I have found various conversions some of which are slow and some of which just produce a white image as a result for some reason. I am providing a list below (I've tried more but I don't remember what exactly):
Using the method below. This conversion works but it takes around 20 ms to convert 640x480 ms Bitmap.
Method (source):
public BitmapImage ToBitmapImage(Bitmap bitmap)
{
using (MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
Using Asmak9.EssentialToolKit library (source), but that conversion took around 27 ms, so that was not an option.
Using the method below. This one does not work for me for some strange reason. It runs with no problem, but the result of the conversion is a blank (white) image and not something that was fed into it.
Method (source):
private BitmapSource Convert(Bitmap bmp)
{
var bitmapData = bmp.LockBits(
new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
var bitmapSource = BitmapSource.Create(
bitmapData.Width, bitmapData.Height,
bitmap.HorizontalResolution, bitmap.VerticalResolution,
PixelFormats.Bgr24, null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
bmp.UnlockBits(bitmapData);
return bitmapSource;
}
Using the method below.This produces the same result as the previous conversion - blank BitmapImage. I am also not sure what could possibly go wrong here.
Method (source):
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private BitmapSource Bitmap2BitmapImage(Bitmap bitmap)
{
IntPtr hBitmap = bitmap.GetHbitmap();
BitmapSource retval;
try
{
retval = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
return retval;
}
Perhaps, the last 2 conversions are better, but they do not convert properly, or I am doing something wrong? Or is there a better way in general to discard the conversion step and display the Bitmap straight?
As Clement pointed out above, Method 3 is the fastest, but it requires 2 things:
Correct pixel format set.
The Convert method should be run in the main thread.
Images extracted using PdfPig are the type of XObject Image or InlineImage (both inherit from IPdfImage). I would like to save and display them in a simple WPF application. In order to do so, I would need to have them in more accessible form, for example BitmapImage format. What is the correct way to achieve that? Library documentation does not help here and my miserable attempts were unsuccessful.
I haven't tested any of this, but it should at least put you on the right path if it doesn't work.
Looking at the PdfPig source on GitHub I can see both XObjectImage and InlineImage have a function TryGetPng. From the looks of it, I would assume that this byte array would match up with the contents of a normal PNG file, which means you should be able to load it straight into a BitmapImage.
Taking some code from this answer. Something like this might work:
InlineImage pdfImage;
byte[] png;
if (pdfImage.TryGetPng(out png))
{
var bitmap = (BitmapSource)new ImageSourceConverter().ConvertFrom(png);
}
Note: both classes also have a TryGetBytes method, which might work in place of TryGetPng. I'm just not sure what format the output of TryGetBytes is in, so I'd be more confident with TryGetPng. Still, I'd try both if one doesn't work.
FWIW, by trial and error, my current approach is to start with TryGetPng and fall back to RawBytes if it fails. I then interpret the extracted bytes as a System.Drawing.Image. I don't use TryGetBytes at all. Here's my code (F#, but should be easy to convert to C#):
let bytes =
match pdfImage.TryGetPng() with
| true, bytes -> bytes
| _ -> Seq.toArray pdfImage.RawBytes
use stream = new MemoryStream(bytes)
use image = Image.FromStream(stream)
I find the following code for me works in most cases. It simply tries all three options available to extract an image (TryGetPng, TryGetBytes and rawBytes) and converts those to an BmpSource.
private static BitmapSource TryGetImage(IPdfImage image)
{
BitmapSource bmp;
byte[] bytes;
if (image.TryGetPng(out bytes))
{
bmp = (BitmapSource)new ImageSourceConverter().ConvertFrom(bytes);
Debug.WriteLine("Converted using TryGetPng.");
}
else
{
IReadOnlyList<byte> iroBytes;
if (image.TryGetBytes(out iroBytes))
{
bmp = (BitmapSource)new ImageSourceConverter().ConvertFrom(bytes);
Debug.WriteLine("Converted using TryGetBytes.");
}
else
{
var rawB=image.RawBytes.ToArray<Byte>();
Bitmap nbmp;
using (var ms = new MemoryStream(rawB))
{
nbmp = new Bitmap(ms);
}
bmp = ConvertBmpToBmpSource(nbmp);
Debug.WriteLine("Converted using RawBytes.");
}
}
return bmp;
}
public static BitmapSource ConvertBmpToBmpSource(Bitmap bitmap)
{
var bitmapData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);
var bitmapSource = BitmapSource.Create(
bitmapData.Width, bitmapData.Height,
bitmap.HorizontalResolution, bitmap.VerticalResolution,
PixelFormats.Bgr24, null,
bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
bitmap.UnlockBits(bitmapData);
return bitmapSource;
}
This is the kinect for xbox 360, using the kinect.dll library.
No problem with the RgbResolution1280x960Fps12 or RgbResolution640x480Fps30 color stream.
The problem occurs with the infrared stream (InfraredResolution640x480Fps30)
Below is the code used and the resulting image.
using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
{
byte[] colorData = null;
if (colorFrame == null) return;
if (colorData == null)
colorData = new byte[colorFrame.PixelDataLength];
colorFrame.CopyPixelDataTo(colorData);
Marshal.FreeHGlobal(colorPtr);
colorPtr = Marshal.AllocHGlobal(colorData.Length);
Marshal.Copy(colorData, 0, colorPtr, colorData.Length);
if (ir) //ir is true if there is infrared stream
{
kinectVideoBitmap = new Bitmap(
colorFrame.Width / 2, //stream in 16 bit
colorFrame.Height,
colorFrame.Width * colorFrame.BytesPerPixel,
System.Drawing.Imaging.PixelFormat.Format32bppRgb, //probable error
colorPtr);
}
else
{
kinectVideoBitmap = new Bitmap(
colorFrame.Width,
colorFrame.Height,
colorFrame.Width * colorFrame.BytesPerPixel,
System.Drawing.Imaging.PixelFormat.Format32bppRgb,
colorPtr);
}
pic.Image = kinectVideoBitmap; //pic is picturebox
byte[] pixelData = new byte[colorFrame.PixelDataLength];
colorFrame.CopyPixelDataTo(pixelData);
An error could be the handling of the PixelFormat, indicated in the code, but I don't think it depends only on that. Below are the image derived from my code, and the one that generates the example app "kinect explorer".
(both images are screenshots)
This is the image of the Kinect explorer example app, you can see how well defined it is in quality and noise, more importantly, even distant objects are well seen.
This is the image of my app, in addition to the various colors deriving from the wrong pixelformat, the noise is such that you cannot see any of the distant objects.
Do you have ideas on how to fix and get a clean image?
I thank everyone in advance.
I make a webrequest to receive a large jpeg as a byte array. This in turn can be converted to a memory stream. I need to get this data into a bitmapdata so that I can marshall copy it to a byte array again. Am i right in assuming that a byte array returned from a memory stream is not the same as a byte array returned from a marshall copy of bitmapdata to a byte array?
I do not want to write the memory stream out to an image as it will return a out of memory error due to its size AND the fact I am using compact cf C# 2.
this is my call to the server..
HttpWebRequest _request = (HttpWebRequest)WebRequest.Create("A url/00249.jpg");
_request.Method = "GET";
_request.Timeout = 5000;
_request.ReadWriteTimeout = 20000;
byte[] _buffer;
int _blockLength = 1024;
int _bytesRead = 0;
MemoryStream _ms = new MemoryStream();
using (Stream _response = ((HttpWebResponse)_request.GetResponse()).GetResponseStream())
{
do
{
_buffer = new byte[_blockLength];
_bytesRead = _response.Read(_buffer, 0, _blockLength);
_ms.Write(_buffer, 0, _bytesRead);
} while (_bytesRead > 0);
}
This is my code to read a byte array from a bitmapdata.
Bitmap Sprite = new Bitmap(_file);
Bitmapdata RawOriginal = Sprite.LockBits(new Rectangle(0, 0, Sprite.Width, Sprite.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
int origByteCount = RawOriginal.Stride * RawOriginal.Height;
SpriteBytes = new Byte[origByteCount];
System.Runtime.InteropServices.Marshal.Copy(RawOriginal.Scan0, SpriteBytes, 0, origByteCount);
Sprite.UnlockBits(RawOriginal);
Note:
I do not want to use this:
Bitmap Sprite = new Bitmap(_file);
I want to go from:
MemoryStream _ms = new MemoryStream();
to
System.Runtime.InteropServices.Marshal.Copy(RawOriginal.Scan0, SpriteBytes, 0, origByteCount);
using what ever conversions are required without writing to a bitmap.
What you're asking is going to be difficult. The data you're receiving from the response object is a full jpeg image, which has a header and then a bunch of compressed data bytes. The byte array addressed by Scan0 is uncompressed and quite possibly includes some padding bytes at the end of each scan line.
Most importantly, you definitely cannot use Marshal.Copy to copy the received bytes to Scan0.
To do what you're asking will require that you parse the header of the jpeg that you receive and uncompress the image bits directly to Scan0, padding each scan line as appropriate. There is nothing in the .NET Framework that will do that for you.
The accepted answer to this question has a link to a library that might help you out.
Even if that works, I'm not certain it will help you out. If calling the BitMap constructor to create the image causes you to run out of memory, it's almost certain that this roundabout method will, as well.
Is the problem that you have so many sprites that you can't keep them all in memory, uncompressed? If so, you'll probably have to find some other way to solve your problem.
By the way, you can save yourself a lot of trouble by changing your code that reads the image to:
MemoryStream _ms = new MemoryStream();
using (Stream _response = ((HttpWebResponse)_request.GetResponse()).GetResponseStream())
{
_response.CopyTo(_ms);
}
I have an image in a memory stream and I want to write this to an MS Excel document, the PIA only exposes the AddPicture method which takes a file path.
Is there away to add a picture without having to write the image to disc?
MSDN
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.shapes.addpicture(v=office.14).aspx
Well, a bit of blind flying but assuming a thing or two about your code (e.g. the source of your stream, data type, etc) this could be a solution:
First, you need to create bitmap image data from the stream (which I assume is a byte stream, also assuming that the stream describes a bitmap image). There's a solution already for that here on Stack Overflow: Byte Array to Bitmap Image I copy-paste the code from the solution:int w= 100;
int h = 200;
int ch = 3; //number of channels (ie. assuming 24 bit RGB in this case)
byte[] imageData = new byte[whch]; //you image data here
Bitmap bitmap = new Bitmap(w,h,PixelFormat.Format24bppRgb);
BitmapData bmData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(imageData,0,pNative,whch);
bitmap.UnlockBits(bmData);
Also assuming you have an object for your workbook and the worksheet you are about to work with, something like this:xlBook = (Excel.Workbook)objExcel.Workbooks.Add("");
xlSheet = (Excel.Worksheet)xlBook.Worksheets1;
xlSheet.Activate();
Now that you have a Bitmap-type variable, and a worksheet, all you need is to paste the image to the sheet:System.Windows.Forms.Clipboard.SetDataObject(bitmap, false);
xlsRange = xlSheet.get_Range((Excel.Range)xlSheet.Cells[5, 15], (Excel.Range)xlSheet.Cells[5, 15]);
xlSheet.Paste(xlsRange, bitmap);
So the key is this guy here (instead of using "AddPicture"): Worksheet.Paste Method
Hope this helps!