locked
Out of Memory..... when displaying Images RRS feed

  • Question

  • Hi, Hoping someone can help me out here... I have a datagridview that contains images,  when I select a row, I want to display the image from that row into a picturebox, and the next two rows into two other pictureboxes. As, I am scrolling through the records I display 3 images at time, but then I start getting "Out of memory" errors. How do I prevent the out of memory and run the code more efficiently. Below are the two procedures I am using...

    the actual error I am getting is out of ConvertToImage...

    Exception:Exception thrown:'System.OutOfMemoryException' in System.Drawing.dll("Out of memory.")

    Private Sub dgvData_SelectionChanged(sender As Object, e As EventArgs) Handles dgvData.SelectionChanged
            Dim img(2) As DataGridViewImageCell
    
            Try
                img(0) = DirectCast(dgvData.Item("FileContents", dgvData.CurrentRow.Index), DataGridViewImageCell)
                img(1) = DirectCast(dgvData.Item("FileContents", dgvData.CurrentRow.Index + 1), DataGridViewImageCell)
                img(2) = DirectCast(dgvData.Item("FileContents", dgvData.CurrentRow.Index + 2), DataGridViewImageCell)
    
    
                Me.pbxRollimage0.Image = ConvertToImage(DirectCast(img(0).Value, Byte()))
                Me.pbxRollimage1.Image = ConvertToImage(DirectCast(img(1).Value, Byte()))
                Me.pbxRollimage2.Image = ConvertToImage(DirectCast(img(2).Value, Byte()))
    
                pbximage0.SizeMode = PictureBoxSizeMode.StretchImage
                pbximage1.SizeMode = PictureBoxSizeMode.StretchImage
                pbximage2.SizeMode = PictureBoxSizeMode.StretchImage
    
                img(0).Dispose()
                img(1).Dispose()
                img(2).Dispose()
    
            Catch ex As Exception
                Console.WriteLine("dgvData_SelectionChanged:{0}", ex.Message)
            End Try
    
        End Sub

        Public Function ConvertToImage(img As Byte()) As Image
            Dim result As Image = Nothing
    
            Dim ms As New System.IO.MemoryStream
    
            Try
                ms.Write(img, 0, img.Length)
                result = New Bitmap(ms)
                ms.Close()
    
            Catch ex As Exception
                Console.WriteLine("ConvertToImage:{0}", ex.Message)
            End Try
            
    	ms.Dispose()
            
    	Return result
    
        End Function

    Tuesday, July 5, 2016 8:33 PM

Answers

  • Ok, I did find a work around for it... setting the CPU to complice for x64.... I noticed that my program as I was scrolling through the images would get up to 2GB in memory and not clear out....  now with it at x64, I get up to 4GB, however I don't get the error there.   

    My question would be.. if I am coping and disposing of the images from the datagridview, why is the app getting up to 2gb anyways?     Still looking for a resoluation.

    here is the data you asked for.... well.. 21 bytes anyway :)

    DirectCast(img(1).Value, Byte()) = {Length=3022362}

     Hi LandLord,

     I don`t know how but, i seemed to overlook the top part of your post replying to me with the image bytes.  So, as the others have already said,  a Using/End Using statement should help which it appears it did.  I would also add that you should Dispose any New Images you added to the PictureBoxes before assigning any more new Images to them.

     For example, check to see if the PictureBox`s Images are not Nothing and Dispose them if they are not.  Then assign the new Images to them. It may help you even further.

            If Me.pbxRollimage0.Image Is Not Nothing Then Me.pbxRollimage0.Image.Dispose
            If Me.pbxRollimage1.Image Is Not Nothing Then Me.pbxRollimage1.Image.Dispose
            If Me.pbxRollimage2.Image Is Not Nothing Then Me.pbxRollimage2.Image.Dispose
    
            Me.pbxRollimage0.Image = ConvertToImage(DirectCast(img(0).Value, Byte()))
            Me.pbxRollimage1.Image = ConvertToImage(DirectCast(img(1).Value, Byte()))
            Me.pbxRollimage2.Image = ConvertToImage(DirectCast(img(2).Value, Byte()))
    

     


    If you say it can`t be done then i`ll try it

    Wednesday, July 6, 2016 5:35 PM
  • apologies Frank L. Smith... I didn't know there were other ways to configure the views on these forums, I'll have to look into that, because sometimes the conversations can be difficult.

    This isn't the answer (in fact it's not even *an* answer), but I thought you'd find this interesting:

    I started working on a class library that would create an internal collection so that all of the source image files - even if the file names are the same - could be reduced to small thumbnail image files and be in the same directory. It also does the image size reduction on another thread and report events along the way (or that's the plan if I ever finish it ;-)).

    I set each of them to be reduced to that the maximum dimension is 75 pixels (width or height, depending on the layout) and the following two images might look unrelated but they're actually one-to-one related. As for file size reduction, have a look yourself at the following screenshots on my website.

    Original Image Files (some of them):

    http://www.fls-online.com/VBNet_Forum/07-06-16/ScreenShot01.png

    Reduced Sized Image Files:

    http://www.fls-online.com/VBNet_Forum/07-06-16/ScreenShot02.png

    So yes it makes a difference! :)

    *****

    Let me know if you're interested and I'll finish this and upload it for you when I'm done.




    In the middle of difficulty ... lies opportunity. -- Albert Einstein

    Wednesday, July 6, 2016 9:16 PM

All replies

  •  Can you show us the first 20 Bytes of each of the  image Byte Arrays?  I have seen this error before because some extra bytes get added to the beginning  of the images bytes.


    If you say it can`t be done then i`ll try it

    Tuesday, July 5, 2016 8:44 PM
  • Ok, I did find a work around for it... setting the CPU to complice for x64.... I noticed that my program as I was scrolling through the images would get up to 2GB in memory and not clear out....  now with it at x64, I get up to 4GB, however I don't get the error there.   

    My question would be.. if I am coping and disposing of the images from the datagridview, why is the app getting up to 2gb anyways?     Still looking for a resoluation.

    here is the data you asked for.... well.. 21 bytes anyway :)

    DirectCast(img(1).Value, Byte()) = {Length=3022362}

     (0): 255
        (1): 216
        (2): 255
        (3): 225
        (4): 72
        (5): 109
        (6): 69
        (7): 120
        (8): 105
        (9): 102
        (10): 0
        (11): 0
        (12): 77
        (13): 77
        (14): 0
        (15): 42
        (16): 0
        (17): 0
        (18): 0
        (19): 8
        (20): 0
        (21): 11

    Tuesday, July 5, 2016 9:00 PM
  • Hmm...  well it appears to be a Jpg image format that was saved which is indicated by the 2 first bytes (255, 216) and it also appears is has Exif data in it too.  So,  it does not appear to be the problem i was thinking it might be.

     Your function does seem to work to convert the bytes of a Jpg image to an Image on my end so,  it seems there has to be something wrong in one or more of your images byte arrays somewhere.  I would start testing by using one image to save to the database or datagridview and  load that same image into a byte array using the IO.File.ReadAllBytes method.

     Then you can compare the Length of both arrays to see if they match.  If the Lengths match, then compare the two arrays to see if the bytes all are the same.  If the Lengths do not match, then you will need to figure out where and how they differ.


    If you say it can`t be done then i`ll try it

    Tuesday, July 5, 2016 9:55 PM
  • You don't mention how many images are initially loaded into your DataGridView. Also no code for that is displayed. Therefore it could be possible that when your app loads all the images then the memory is being used up due to not releasing objects that may be used to load the DataGridView with images in its image column. Or maybe you have a huge amount of images loaded into the DataGridView and therefore alot of memory is in use.

    I doubt this is an issue but it is possible. In your below code the memory stream created will not be closed if the Try/Catch occurs.

    This probably is not an issue but it may be that result should be disposed of after it is returned, unless that occurs naturally which I am uncertain, to be garbage collected.

    Update: On another note result is an Image type although I suppose an image type and bitmap type can be intertwined or whatever. Perhaps it would be best to use Bitmaps rather than Images for the project in this area. I'm not certain really.

        Public Function ConvertToImage(img As Byte()) As Image
            Dim result As Image = Nothing
    
            Dim ms As New System.IO.MemoryStream
    
            Try
                ms.Write(img, 0, img.Length)
                result = New Bitmap(ms)
                ms.Close()
    
            Catch ex As Exception
                Console.WriteLine("ConvertToImage:{0}", ex.Message)
            End Try
            
    	ms.Dispose()
            
    	Return result
    
        End Function


    La vida loca

    Tuesday, July 5, 2016 10:52 PM
  • LL,

    You're not disposing it from the control (the PictureBox), so there's still a reference to it.


    In the middle of difficulty ... lies opportunity. -- Albert Einstein

    Tuesday, July 5, 2016 10:57 PM
  • In order to reduce the memory usage, try the next solution too:

      Using ms As New MemoryStream(img)

        result = New Bitmap(ms)

      End Using

    The OutOfMemory error in case of images does not always mean a memory problem. In order to check the data if the problem persists, try saving the img array to some file using File.WriteAllBytes, then open it using Paint, for example.

    Wednesday, July 6, 2016 7:06 AM
  • In order to reduce the memory usage, try the next solution too:

      Using ms As New MemoryStream(img)

        result = New Bitmap(ms)

      End Using

    The OutOfMemory error in case of images does not always mean a memory problem. In order to check the data if the problem persists, try saving the img array to some file using File.WriteAllBytes, then open it using Paint, for example.


    Yes you may be trying to make an image that is 20000x200000 or you may have an infinite/recursive loop. Check the size of the image when you get the error and look at the call stack and see if you are in an infinite loop.

    Make sure you have vb set to break on CLR errors in the Debug - Exceptions. Also remove your try catch.

    Also be sure to dispose everything as the others have shown.

    Wednesday, July 6, 2016 10:04 AM
  • I will try this... the "using" statement is a huge improvement in memory processed. in x86 I get up to 1Gb.. the scrolling the images is between .5Gb and 1GB.  I'll still get the out of memory error from time to time.  When I have the CPU in x64, max memory used is 2GB, but sits around 1GB when scrolling and I've not been able to get an Out of memory error this way.  Well continue testing this later this morning...

    Thanks for all the suggestions and ideas

    Wednesday, July 6, 2016 12:32 PM
  • ...the scrolling the images is between .5Gb and 1GB. 

    Before you do anything else, reduce the size of those images. You can't show them that large anyway.

    If you have a need to keep the original intact in your db, then create a new column for a thumbnail and use that to display.


    In the middle of difficulty ... lies opportunity. -- Albert Einstein

    Wednesday, July 6, 2016 12:38 PM
  • that was my memory usage, with about 77 records.. (pictures) in my datagridview
    Wednesday, July 6, 2016 1:25 PM
  • but that is an interesting idea though, I have the pictures loaded in the datagridview. How would I change the size(length) of the data to display like a large thumbnail view?
    Wednesday, July 6, 2016 1:26 PM
  • but that is an interesting idea though, I have the pictures loaded in the datagridview. How would I change the size(length) of the data to display like a large thumbnail view?

    Please make it obvious who you're addressing. We don't all see this forum the same way.

    You can do it a number of ways but I'd create a new image (use .jpg) and store the file that's the right size to start with - then the PictureBox can be set to center it rather than having it scale it to display it.


    In the middle of difficulty ... lies opportunity. -- Albert Einstein

    Wednesday, July 6, 2016 1:31 PM
  • apologies Frank L. Smith... I didn't know there were other ways to configure the views on these forums, I'll have to look into that, because sometimes the conversations can be difficult.
    Wednesday, July 6, 2016 1:46 PM
  • apologies Frank L. Smith... I didn't know there were other ways to configure the views on these forums, I'll have to look into that, because sometimes the conversations can be difficult.

    I'm just Frank. :)

    *****

    When you do that, work out the correct dimensions needed to maintain the aspect ratio (which will be different for portrait or landscape) and you'll see that the resultant file will be MUCH smaller (much).


    In the middle of difficulty ... lies opportunity. -- Albert Einstein

    Wednesday, July 6, 2016 1:52 PM
  • Ok, I did find a work around for it... setting the CPU to complice for x64.... I noticed that my program as I was scrolling through the images would get up to 2GB in memory and not clear out....  now with it at x64, I get up to 4GB, however I don't get the error there.   

    My question would be.. if I am coping and disposing of the images from the datagridview, why is the app getting up to 2gb anyways?     Still looking for a resoluation.

    here is the data you asked for.... well.. 21 bytes anyway :)

    DirectCast(img(1).Value, Byte()) = {Length=3022362}

     Hi LandLord,

     I don`t know how but, i seemed to overlook the top part of your post replying to me with the image bytes.  So, as the others have already said,  a Using/End Using statement should help which it appears it did.  I would also add that you should Dispose any New Images you added to the PictureBoxes before assigning any more new Images to them.

     For example, check to see if the PictureBox`s Images are not Nothing and Dispose them if they are not.  Then assign the new Images to them. It may help you even further.

            If Me.pbxRollimage0.Image Is Not Nothing Then Me.pbxRollimage0.Image.Dispose
            If Me.pbxRollimage1.Image Is Not Nothing Then Me.pbxRollimage1.Image.Dispose
            If Me.pbxRollimage2.Image Is Not Nothing Then Me.pbxRollimage2.Image.Dispose
    
            Me.pbxRollimage0.Image = ConvertToImage(DirectCast(img(0).Value, Byte()))
            Me.pbxRollimage1.Image = ConvertToImage(DirectCast(img(1).Value, Byte()))
            Me.pbxRollimage2.Image = ConvertToImage(DirectCast(img(2).Value, Byte()))
    

     


    If you say it can`t be done then i`ll try it

    Wednesday, July 6, 2016 5:35 PM
  • apologies Frank L. Smith... I didn't know there were other ways to configure the views on these forums, I'll have to look into that, because sometimes the conversations can be difficult.

    This isn't the answer (in fact it's not even *an* answer), but I thought you'd find this interesting:

    I started working on a class library that would create an internal collection so that all of the source image files - even if the file names are the same - could be reduced to small thumbnail image files and be in the same directory. It also does the image size reduction on another thread and report events along the way (or that's the plan if I ever finish it ;-)).

    I set each of them to be reduced to that the maximum dimension is 75 pixels (width or height, depending on the layout) and the following two images might look unrelated but they're actually one-to-one related. As for file size reduction, have a look yourself at the following screenshots on my website.

    Original Image Files (some of them):

    http://www.fls-online.com/VBNet_Forum/07-06-16/ScreenShot01.png

    Reduced Sized Image Files:

    http://www.fls-online.com/VBNet_Forum/07-06-16/ScreenShot02.png

    So yes it makes a difference! :)

    *****

    Let me know if you're interested and I'll finish this and upload it for you when I'm done.




    In the middle of difficulty ... lies opportunity. -- Albert Einstein

    Wednesday, July 6, 2016 9:16 PM