How to use Parallel.ForEach with image object? - c#

Now my method is :
public static byte[] ImageToByte(Image img)
{
byte[] byteArray = new byte[0];
using (MemoryStream stream = new MemoryStream())
{
img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
stream.Close();
byteArray = stream.ToArray();
}
return byteArray;
}
after read this. And i still confuse and can't find a way to use this.
So my question is how to use Parallel.ForEach or Parallel anything with my method.
my goal is to speed this method by using more cores of CPU to speed this up anything advice?
ps. I not serious if i can do the parallel with my method and that not speed up anything
I just want to try this and record the result Thank all your guy .

You can only use the parallel processing if you have multiple images.
Imagine going through seperate images in a for each loop you could do this:
Parallel.ForEach(images, img =>
{
byte[] byteArray = new byte[0];
using (MemoryStream stream = new MemoryStream())
{
img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
stream.Close();
byteArray = stream.ToArray();
}
});

If you really want to speed up the code you could do :
BitmapData d = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int length = Math.Abs(d.Stride) * image.Height;
byte[] buff = new byte[length];
Marshal.Copy(d.Scan0, buff, 0, length);
image.UnlockBits(d);
return buff;

Related

WCF: using memorystream to transfer bitmaps in C#

I am using WCF for communication between client and server program on the same machine. Earlier I used Bitmaps to transfer video frames to the client. It was successful with WCF, but I only have 6 megabytes / sec LAN. Transferring 1.2Mb every second would block the LAN. I decided to JPEG encode the captured Bitmaps from the camera. It is possible to save a JPEG on the disk from a bitmap, or to a Memorystream. I created a
[MessageContract]
public class RemoteResponse
{
[MessageBodyMember(Order = 1)]
public System.IO.MemoryStream jpegImage;
}
I return this back from the (I)LiveImageService.
As I get it out in the client: RemoteResponse ret = client.GetLiveImage();
MemoryStream returnedImage = returnedResponse.jpegImage;
returnedImage.Position = 0L;
returnedImage.Seek(0, SeekOrigin.Begin);
args.image = returnedImage;
I do this in the event handler:
args.image.Position = 0L;
args.image.Seek(0, SeekOrigin.Begin);
//bitmap = new Bitmap(args.image);
bitmap = (Bitmap)Image.FromStream(args.image);
It gives me a "parameter is not valid" exception.
I have read that this is a buggy solution of Microsoft. Actually the
retreived stream is believed to be 21 bytes long because there is a trailing
zero \0 at the end of the leading "System.Drawing.Bitmap"\0. That is why the
processing of the image is stopped.
I have read a solution that avoids this:
byte[] arr = PdfReader.FlateDecode(PdfReader.GetStreamBytesRaw((PRStream)obj), true);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(width, height, pixelFormat);
System.Drawing.Imaging.BitmapData bmd = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.WriteOnly, pixelFormat);
Marshal.Copy(arr, 0, bmd.Scan0, arr.Length);
bmp.UnlockBits(bmd);
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); // here it is the trick
arr = ms.GetBuffer(); // now I replace my incorrect image format bytes for the correct ones
System.Drawing.Image image = System.Drawing.Image.FromStream(ms);
image.Save(path, System.Drawing.Imaging.ImageFormat.Png);
}
How does the last arr = ... have any effect? It is not used anymore later?
So I don't understand.
update:
On a Microsoft website: https://social.msdn.microsoft.com/Forums/vstudio/en-US/f9e39595-04ca-42ae-a353-eb1a08602631/resolved-creating-image-file-parameter-is-not-valid?forum=netfxbcl
I rewrote the example so:
public static Bitmap DecodeJPEGFromMemoryStream(MemoryStream stream, int width, int height, PixelFormat pixelFormat)
{
string content = System.Text.Encoding.UTF8.GetString(stream.ToArray());
byte[] arr = Encoding.UTF8.GetBytes(content);
//byte[] arr = PdfReader.FlateDecode(PdfReader.GetStreamBytesRaw((PRStream)obj), true);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(width, height, pixelFormat);
System.Drawing.Imaging.BitmapData bmd = bmp.LockBits(new
System.Drawing.Rectangle(0, 0, width, height),
System.Drawing.Imaging.ImageLockMode.WriteOnly, pixelFormat);
Marshal.Copy(arr, 0, bmd.Scan0, arr.Length);
bmp.UnlockBits(bmd);
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); // here it is the trick
arr = ms.GetBuffer(); // now I replace my incorrect image format bytes for the correct ones
System.Drawing.Image image = System.Drawing.Image.FromStream(ms);
Bitmap ret = new Bitmap(image);
return ret;
}
}
I can see the image in the Picturebox, but it has only 2 lines, flickering.
If I obscure the camera I see that it darkens. So the data comes through now. But I cannot reconstruct my JPEG live data.
I was able to decode the JPEG data, only with saving it to disk.
So I've got the image without error. I tried to save the image only
temporarily and delete after.
public static Bitmap DecodeJPEGFromMemoryStream(MemoryStream stream, int width, int height, PixelFormat pixelFormat)
{
try
{
if(File.Exists("c:\\imagecache.jpg") == true)
File.Delete("c:\\imagecache.jpg");
//using (FileStream file = new FileStream("c:\\imagecache.jpg", FileMode.CreateNew, System.IO.FileAccess.ReadWrite))
using (FileStream file = File.Open("c:\\imagecache.jpg", FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite))
{
stream.WriteTo(file);
//JpegBitmapDecoder decoder = new JpegBitmapDecoder(file, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
file.Close();
file.Dispose();
}
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
//BitmapSource bitmapSource = decoder.Frames[0];
Bitmap jpeg;
{
{
FileInfo info = new FileInfo("c:\\imagecache.jpg");
if (info.Length == 0)
{
File.Delete("c:\\imagecache.jpg");
return null;
}
}
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
jpeg = (Bitmap)Image.FromFile("c:\\imagecache.jpg"); //the process cannot access the file...
}
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
if (File.Exists("c:\\imagecache.jpg") == true)
File.Delete("c:\\imagecache.jpg");
return jpeg;
}
catch(Exception ex)
{
return null;
}
}
I get the exception: The process cannot access the file "imagecache.jpg",
because it is being used by another process. Exception comes at the FromFile.
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
Bitmap retImage = (Bitmap)jpeg.Clone();
jpeg.Dispose();
if (File.Exists("c:\\imagecache.jpg") == true)
{
System.IO.FileInfo fi = new System.IO.FileInfo("c:\\imagecache.jpg");
fi.Delete();
}
return retImage;
}
catch (Exception ex)
{
return null;
}
finally
{
stream.Dispose();
}
It is fixed so. Image.FromFile: the File remains locked until the Image is disposed.
So I cloned my Image and disposed the old one.
Before I've seen my Image, now nothing is visible.

BitmapImage to byte[] - C# web

I need to convert a BitmapImage in a byte[] but I don't find how to do it in C# web.
I found examples but none of them work (JpegBitmapEncoder doesn't exist, BitmapImageObject.StreamSource doesn't exist, there isn't WriteableBitmap constructor with BitmapImage as parameter, Extensions.SaveJpeg(parameters) doesn't exist ...).
Examples I found:
Constructor new WriteableBitmap(bitmapImage) doesn't exist.
public static byte[] ConvertToBytes(this BitmapImage bitmapImage)
{
byte[] data;
// Get an Image Stream
using (MemoryStream ms = new MemoryStream())
{
WriteableBitmap btmMap = new WriteableBitmap(bitmapImage);
// write an image into the stream
Extensions.SaveJpeg(btmMap, ms,
bitmapImage.PixelWidth, bitmapImage.PixelHeight, 0, 100);
// reset the stream pointer to the beginning
ms.Seek(0, 0);
//read the stream into a byte array
data = new byte[ms.Length];
ms.Read(data, 0, data.Length);
}
//data now holds the bytes of the image
return data;
}
new WriteableBitmap(img), System.Windows.Media.Imaging.Extensions.SaveJpeg don't exist.
public static byte[] ImageToBytes(BitmapImage img)
{
using (MemoryStream ms = new MemoryStream())
{
WriteableBitmap btmMap = new WriteableBitmap(img);
System.Windows.Media.Imaging.Extensions.SaveJpeg(btmMap, ms, img.PixelWidth, img.PixelHeight, 0, 100);
img = null;
return ms.ToArray();
}
}
imageSource.StreamSource doesn't exist.
public static byte[] ImageToByte(BitmapImage imageSource)
{
Stream stream = imageSource.StreamSource;
Byte[] buffer = null;
if (stream != null && stream.Length > 0)
{
using (BinaryReader br = new BinaryReader(stream))
{
buffer = br.ReadBytes((Int32)stream.Length);
}
}
return buffer;
}
JpegBitmapEncoder doesn't exist.
byte[] data;
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
using(MemoryStream ms = new MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
Try with the using statement to a namespace in the beginning of your code.
Otherwise there should be some Nuget packages which you could install to achieve your goal.
using System.Drawing;
In Main method
Image img = Image.FromFile("path to the file");
var byteArray = ImageToByte(img);
public static byte[] ImageToByte(Image img)
{
ImageConverter converter = new ImageConverter();
return (byte[])converter.ConvertTo(img, typeof(byte[]));
}
Try this
I think this will help...
public byte[] ConvertBitMapToByteArray(Bitmap bitmap)
{
byte[] result = null;
if (bitmap != null)
{
MemoryStream stream = new MemoryStream();
bitmap.Save(stream, bitmap.RawFormat);
result = stream.ToArray();
}
return result;
}
byte[] foo = System.IO.File.ReadAllBytes("bitmap path");
Or
byte[] foo;
Object obj = YourBitmap;
BinaryFormatter bf = new BinaryFormatter();
using (var ms = new MemoryStream())
{
bf.Serialize(ms, obj);
foo = ms.ToArray();
}
Or
ImageConverter foocon = new ImageConverter();
byte[] foo = (byte[])foocon.ConvertTo(YourBitmap, typeof(byte[]));
Or
MemoryStream ms = new MemoryStream();
Bitmap.Save(ms, YourBitmap.RawFormat);
byte[] foo = ms.ToArray();
Finally, it seems that, obviously, it missed some libraries but we are limited with our application, so we decided to recover our pictures by another way. Anyway, thank you all.

Read First 6 Bytes From NetworkStream?

I am sending an image(a screenshot, particularly) via a tcpclient/networkstream. To read the image bytes properly on the receiving end, I need to know the length of the image. What I intend to do is save the first 6 bytes of the buffer for the image size(since it seems to never exceed 6 numbers), and then from thereon have the rest of the buffer as the image. The problem I am having is I am not able to read only the first 6 bytes.
Server Code
int data = 0;
byte[] readBuffer = new byte[getSelectedClient().ReceiveBufferSize];
**data = stream.Read(readBuffer, 0, 5, readBuffer.Length);** <-- sort of thing im trying to do
string pictureSize = Encoding.ASCII.GetString(readBuffer, 0, data);
Console.WriteLine(pictureSize); //for debugging purposes
string x = new Random().Next().ToString();
FileStream f = new FileStream(x + ".bmp", FileMode.Create, FileAccess.Write);
while (new FileInfo(x + ".bmp").Length != Convert.ToInt32(pictureSize))
{
**data = stream.Read(readBuffer, 6, readBuffer.Length);** <-- then it would read from the 6th byte, which would be the start of the image
f.Write(readBuffer, 0, data);
}
f.Close();
Process.Start(x + ".bmp");
screenShotBTN.Enabled = true;
Client Code
MemoryStream ms = new MemoryStream();
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
writeToStream(combineBytes(Encoding.ASCII.GetBytes(ms.Length.ToString()), ms.ToArray()));
ms.Close();
public static byte[] combineBytes(byte[] b1, byte[] b2)
{
byte[] b3 = new byte[b1.Length + b2.Length];
Array.Copy(b1, 0, b3, 0, b1.Length);
Array.Copy(b2, 0, b3, b1.Length, b2.Length);
return b3;
}
What you are implementing is Length Prefixed messaging.
Things to remember are that when you are reading from a network stream, you will not necessarily receive all the bytes that you are looking for immediately (most times when you are testing locally everything will, but you need to program for the possibility you'll only receive 1 byte at the worst case).
Therefore you should go into a state (awaitinglength, awaitingpayload).
Then you commence reading byte(s) while in the awaiting length, then build up that data in a buffer. Once you have your length (6 bytes in your case), then you can switch state to awaitingpayload, then read this into your secondary buffer until you have the full payload and then you are good to go.
It might be worthwhile to have a read of Stephen Cleary's articles on message framing. I learn't a lot about networking through reading these.
http://blog.stephencleary.com/2009/04/sample-code-length-prefix-message.html
I solved the issue on my own.
Server
int data = 0;
byte[] readBuffer = new byte[getSelectedClient().ReceiveBufferSize];
data = stream.Read(readBuffer, 0, 6); //only read first 6
string pictureSize = Encoding.ASCII.GetString(readBuffer, 0, data);
Console.WriteLine(pictureSize); //for debugging purposes
string x = new Random().Next().ToString();
FileStream f = new FileStream(x + ".bmp", FileMode.Create, FileAccess.Write);
while (new FileInfo(x + ".bmp").Length != Convert.ToInt32(pictureSize))
{
data = stream.Read(readBuffer, 0, readBuffer.Length);
f.Write(readBuffer, 0, data);
}
f.Close();
Process.Start(x + ".bmp");
screenShotBTN.Enabled = true;
Client code
MemoryStream ms = new MemoryStream();
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
Console.WriteLine(ms.Length.ToString());
writeToStream(combineBytes(Encoding.ASCII.GetBytes(ms.Length.ToString()), ms.ToArray()));
ms.Close();

Convert bitmapimage into byte array

I am trying to convert bitmap Image to Byte array. I have select all the image by using MediaLibrary class and added it into a list of bitmap images. Here is my code
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!store.DirectoryExists("ImagesZipFolder"))
{
store.CreateDirectory("ImagesZipFolder");
for (int i = 0; i < imgname.Count(); i++)
{
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(#"ImagesZipFolder\" + imgname[i], System.IO.FileMode.CreateNew, store))
{
byte[] bytes = null;
using (MemoryStream ms = new MemoryStream())
{
WriteableBitmap wBitmap = new WriteableBitmap(ImgCollection[i]);
wBitmap.SaveJpeg(ms, wBitmap.PixelWidth, wBitmap.PixelHeight, 0, 100);
stream.Seek(0, SeekOrigin.Begin);
bytes = ms.GetBuffer();
stream.Write(bytes, 0, bytes.Length);
}
// byte[] bytes = Encoding.UTF8.GetBytes(imgname[i]);//new byte[ImgCollection[i].PixelWidth * ImgCollection[i].PixelHeight * 4];
// stream.Write(bytes, 0, bytes.Length);
}
}
}
else {
directory = true;
}
}
Basically what I am trying to do is, selecting all images or photo from device and create a zip file of that images. I was successful in creating a zip file of images. When I extract that file there is some images, but the problem is when I double click on image, I can't see that image. I think the problem is in reading the bytes of image. I am not getting what's wrong? Is my code is correct ?
Perhaps you can try the below. I know this code maintains the image, so if you have no luck using this, you may have a different issue.
// Convert the new image to a byte[]
ImageConverter converter = new ImageConverter();
byte[] newBA = (byte[])converter.ConvertTo(newImage, typeof(byte[]));
The ImageConverter is of the System.Drawing namespace.
Update:
http://msdn.microsoft.com/en-GB/library/system.windows.media.imagesourceconverter.convertto.aspx
You should be able to use this in place of the System.Drawing type I suggested.
There is no need to save the WriteableBitmap to a MemoryStream and then copy it to an IsolatedStorageFileStream. Just save the bitmap directly to the IsolatedStorageFileStream.
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(#"ImagesZipFolder\" + imgname[i], System.IO.FileMode.CreateNew, store))
{
WriteableBitmap wBitmap = new WriteableBitmap(ImgCollection[i]);
wBitmap.SaveJpeg(stream, wBitmap.PixelWidth, wBitmap.PixelHeight, 0, 100);
}
This will allow you to save on memory as well. If you really want to save memory, you could reuse the WriteableBitmap.

BitmapImage to byte[]

I have a BitmapImage that I'm using in a WPF application, I later want to save it to a database as a byte array (I guess it's the best way), how can I perform this conversion?
Or, alternatively, is there a better way to save a BitmapImage (or any of its base classes, BitmapSource or ImageSource) to a data repository?
To convert to a byte[] you can use a MemoryStream:
byte[] data;
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
using(MemoryStream ms = new MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
Instead of the JpegBitmapEncoder you can use whatever BitmapEncoder you like as casperOne said.
If you are using MS SQL you could also use a image-Column as MS SQL supports that datatype, but you still would need to convert the BitmapImage somehow.
You will have to use an instance of a class that derives from BitmapEncoder (such as BmpBitmapEncoder) and call the Save method to save the BitmapSource to a Stream.
You would choose the specific encoder depending on the format you want to save the image in.
write it to a MemoryStream, then you can access the bytes from there.
something kinda like this:
public Byte[] ImageToByte(BitmapImage imageSource)
{
Stream stream = imageSource.StreamSource;
Byte[] buffer = null;
if (stream != null && stream.Length > 0)
{
using (BinaryReader br = new BinaryReader(stream))
{
buffer = br.ReadBytes((Int32)stream.Length);
}
}
return buffer;
}
You can give a formate of bitmap:
Image bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics.FromImage(bmp).CopyFromScreen(new Point(0, 0), new Point(0, 0), Screen.PrimaryScreen.Bounds.Size);
MemoryStream m = new MemoryStream();
bmp.Save(m, System.Drawing.Imaging.ImageFormat.Png);
byte[] imageBytes = m.ToArray();
string base64String = Convert.ToBase64String(imageBytes);
Just use a MemoryStream.
byte[] data = null;
using(MemoryStream ms = new MemoryStream())
{
bitmapImage.Save(ms);
data = ms.ToArray();
}

Categories

Resources