4

I am developing one windows phone application which useful for upload images to web server. I am selecting all images from my device into one List object. I am converting all bitmap image to byte[] one by one.

My code

public byte[] ConvertToBytes(BitmapImage bitmapImage)
    {
        byte[] data = null;
        WriteableBitmap wBitmap = null;

        using (MemoryStream stream = new MemoryStream())
        {
            wBitmap = new WriteableBitmap(bitmapImage);
            wBitmap.SaveJpeg(stream, wBitmap.PixelWidth, wBitmap.PixelHeight, 0, 100);
            stream.Seek(0, SeekOrigin.Begin);
            //data = stream.GetBuffer();
            data = stream.ToArray();
            DisposeImage(bitmapImage);
            return data;
        }

    }
    public void DisposeImage(BitmapImage image)
    {
        if (image != null)
        {
            try
            {
                using (MemoryStream ms = new MemoryStream(new byte[] { 0x0 }))
                {
                    image.SetSource(ms);
                }
            }
            catch (Exception ex)
            {
            }
        }
    }

Conversion from bitmap to byte

 using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
        {
            if (!store.DirectoryExists("ImagesZipFolder"))
            {
                //MediaImage mediaImage = new MediaImage();
                //mediaImage.ImageFile = decodeImage(new byte[imgStream[0].Length]);
                //lstImages.Items.Add(mediaImage);

                store.CreateDirectory("ImagesZipFolder");
                for (int i = 0; i < imgname.Count(); i++)
                {
                    using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(@"ImagesZipFolder\" + imgname[i], FileMode.CreateNew,store))
                    //using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(@"ImagesZipFolder\text.txt" , System.IO.FileMode.OpenOrCreate, store))                        
                    {
                       // byte[] bytes = new byte[imgStream[i].Length];
                        byte[] bytes = ConvertToBytes(ImgCollection[i]);
                        stream.Write(bytes, 0, bytes.Length);
                    }
                }
            }
            else {
                directory = true;
            }
          }

I have 91 images in my emulator. When I am converting all these bitmap images into byte[], get's following error on line wBitmap = new WriteableBitmap(bitmapImage);

An exception of type 'System.OutOfMemoryException' occurred in System.Windows.ni.dll but was not handled in user code

Error

What can I do to solve this error?

Web Service

If we are sending huge file to web service, is it gives error like

An exception of type 'System.OutOfMemoryException' occurred in System.ServiceModel.ni.dll but was not handled in user code

14
  • 1
    Do you need to have all byte arrays in memory at the same time? Maybe it is possible to convert image to bytes and upload one by one. Commented Jul 30, 2013 at 12:52
  • Yes I want it at a time. I am creating a zip file of that all images Commented Jul 30, 2013 at 12:54
  • So you are loading 91 images into memory? No surprise you are running out of it. Commented Jul 30, 2013 at 12:57
  • What is more be careful with GetBuffer - msdn: Note that the buffer contains allocated bytes which might be unused. For example, if the string "test" is written into the MemoryStream object, the length of the buffer returned from GetBuffer is 256, not 4, with 252 bytes unused. To obtain only the data in the buffer, use the ToArray method; however, ToArray creates a copy of the data in memory. Commented Jul 30, 2013 at 12:58
  • Just fyi, there are bitmaps which can not be loaded into memeory on mid-end PC-s. I mean single bitmap. Thats how much memory they can consume. And you load an array of 91 on mobile device -_- Commented Jul 30, 2013 at 13:00

3 Answers 3

2

What can I do to solve this error?

Change the way you do things, to use less memory.

For instance, instead of converting every picture then uploading them, convert a picture, upload it, convert the next, and so on.

If you really want to process all the pictures at once, you can store the byte arrays in the isolated storage rather than keeping them in memory, and read them back when needed.

Basically, rethink your process and use the storage to use less memory at a given time.

That or ask Microsoft to lift the memory limit on Windows Phone, but it may be a tad trickier.

Sign up to request clarification or add additional context in comments.

9 Comments

Thanks for reply. I am doing same as you say. I am passing image one by one and writing it in isolated storage.
Hey KooKiz, I have checked this link stackoverflow.com/questions/13355496/… and added "DisposeImage" method in my code. I am calling this method before return in "ConvertToBytes" method. But I am getting this error "The component cannot be found. (Exception from HRESULT: 0x88982F50)"
Hello, I am getting the error on a line "using (MemoryStream ms = new MemoryStream(new byte[] { 0x0 }))"
@Ajay Then it's by design, the try/catch is there to swallow this exception. The BitmapImage will keep consuming memory until a new picture is loaded. The very purpose of this hack is to load an invalid picture to force the BitmapImage to free the memory previously used, and yet don't use memory for the new picture (since it's invalid and cannot be loaded). The exception slows down the execution when the debugger is attached (when you start the application directly from Visual Studio), but no impact is noticeable when running without Visual Studio.
That said, just nulling the image source (image.Source = null;) is sufficient in most cases. Except in some obscure conditions, like in the question linked, where even nulling the source isn't enough to force the runtime to free the memory.
|
1

The problem lies within how the GC and bitmap images work together as described in this bug report: https://connect.microsoft.com/VisualStudio/feedback/details/679802/catastrophic-failure-exception-thrown-after-loading-too-many-bitmapimage-objects-from-a-stream#details

From the report:

When Silverlight loads an image, the framework keeps a reference and caches the decoded image until flow control is returned to the UI thread dispatcher. When you load images in a tight loop like that, even though your application doesn't retain a reference, the GC can't free the image until we release our reference when flow control is returned.

After processing 20 or so images, you could stop and queue the next set using Dispatcher.BeginInvoke just to break up the work that is processed in one batch. This will allow us to free images that aren't retained by your application.

I understand with the current decode behavior it's not obvious that Silverlight is retaining these references, but changing the decoder design could impact other areas, so for now I recommend processing images like this in batches.

Now, if you're actually trying to load 500 images and retain them, you are still likely to run out of memory depending on image size. If you're dealing with a multi-page document, you may want to instead load pages on demand in the background and release them when out of view with a few pages of buffer so that at no point do you exceed reasonable texture memory limits.

Fix:

private List<BitmapImage> Images = .....;
private List<BitmapImage>.Enumerator iterator;
private List<byte[]> bytesData = new List<byte[]>();

public void ProcessImages()
{
    if(iterator == null)
        iterator = Images.GetEnumerator();

    if(iterator.MoveNext())
    {
        bytesData.Add(ConvertToBytes(iterator.Current));

        //load next images
        Dispatcher.BeginInvoke(() => ProcessImages());
    }else{
        //all images done
    }
}

public static byte[] ConvertToBytes(BitmapImage bitmapImage)
{
    using (MemoryStream stream = new MemoryStream())
    {
        var wBitmap = new WriteableBitmap(bitmapImage);
        wBitmap.SaveJpeg(stream, wBitmap.PixelWidth, wBitmap.PixelHeight, 0, 100);
        stream.Seek(0, SeekOrigin.Begin);
        return stream.ToArray();
    }
}

1 Comment

I have tried this solution but I am getting same error on same line. :( Please check edited question
0

I have found new way to convert Bitmap Image to byte[] array. There is no need to write image in memory. Hare is code

 public byte[] GetBytes(BitmapImage bi)
    {
        WriteableBitmap wbm = new WriteableBitmap(bi);
        return ToByteArray(wbm);
    }
    public byte[] ToByteArray(WriteableBitmap bmp)
    {
        // Init buffer
        int w = bmp.PixelWidth;
        int h = bmp.PixelHeight;
        int[] p = bmp.Pixels;
        int len = p.Length;
        byte[] result = new byte[4 * w * h];

        // Copy pixels to buffer
        for (int i = 0, j = 0; i < len; i++, j += 4)
        {
            int color = p[i];
            result[j + 0] = (byte)(color >> 24); // A
            result[j + 1] = (byte)(color >> 16); // R
            result[j + 2] = (byte)(color >> 8);  // G
            result[j + 3] = (byte)(color);       // B
        }

        return result;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.