2013-07-23 12 views
5

Sto tentando di ridimensionare le immagini in un processo batch. Quando uso le classi fornite da .Net, la memoria non viene rilasciata correttamente, quindi viene lanciata OutOfMemoryException. Penso di usare le istruzioni correttamente. Il codice è qui sotto:. Net Image ridimensionamento della perdita di memoria

private static byte[] Resize(byte[] imageBytes, int width, int height) 
    { 
      using (var img = Image.FromStream(new MemoryStream(imageBytes))) 
      { 
       using (var outStream = new MemoryStream()) 
       { 
        double y = img.Height; 
        double x = img.Width; 

        double factor = 1; 
        if (width > 0) 
         factor = width/x; 
        else if (height > 0) 
         factor = height/y; 

        var imgOut = new Bitmap((int)(x * factor), (int)(y * factor)); 
        var g = Graphics.FromImage(imgOut); 
        g.Clear(Color.White); 
        g.DrawImage(img, new Rectangle(0, 0, (int)(factor * x), 
                (int)(factor * y)), 
           new Rectangle(0, 0, (int)x, (int)y), GraphicsUnit.Pixel); 

        imgOut.Save(outStream, ImageFormat.Jpeg); 

        return outStream.ToArray(); 
       } 
      } 
     } 

In alternativa a questo codice è possibile utilizzare la libreria di FreeImage. Quando utilizzo FreeImage, non c'è alcun problema di memoria. Codice con FreeImage:

private static byte[] Resize(byte[] imageBytes, int width, int height) 
    { 
     var img = new FIBITMAP(); 
     var rescaled = new FIBITMAP(); 
     try 
     { 
      using (var inStream = new MemoryStream(imageBytes)) 
      { 
       img = FreeImage.LoadFromStream(inStream); 
       rescaled = FreeImage.Rescale(img, width, height, FREE_IMAGE_FILTER.FILTER_BICUBIC); 

       using (var outStream = new MemoryStream()) 
       { 
        FreeImage.SaveToStream(rescaled, outStream, FREE_IMAGE_FORMAT.FIF_JPEG); 
        return outStream.ToArray(); 
       } 
      } 
     } 
     finally 
     { 
      if (!img.IsNull) 
       FreeImage.Unload(img); 

      img.SetNull(); 

      if (!rescaled.IsNull) 
       FreeImage.Unload(rescaled); 

      rescaled.SetNull(); 
     } 
    } 

Cosa manca nel mio primo codice?

+2

non si utilizza un utilizzo sulla bitmap .... anche voi volete mettere un utilizzo sulla grafica g troppo. –

+1

Non hai gettato l'oggetto 'Graphics' (' var g = Graphics.FromImage (imgOut); ') –

+0

o sull'oggetto' g' - un oggetto Graphics – SteveLove

risposta

5

Credo che la vostra perdita è con le seguenti due righe:

var imgOut = new Bitmap((int)(x * factor), (int)(y * factor)); 
var g = Graphics.FromImage(imgOut); 

Sia Bitmap e Graphics implementare IDisposable e devono essere smaltiti quando si è finito di usarli.

vorrei suggerire entrambi avvolgendo in un blocco using:

using(imgOut = new Bitmap((int)(x * factor), (int)(y * factor))) 
{ 
    using(var g = Graphics.FromImage(imgOut)) 
    { 
     //rest of code... 
    } 
} 

Here is a list of GDI objects di tenere d'occhio per, assicurarsi di pulirli correttamente se si utilizzano.

+0

anche, 'using (var g = Graphics.FromImage (imgOut))' ... –

+0

@AlexFilipovici: Sì, ho notato i commenti. Non ero a conoscenza della necessità di eliminarlo, quindi lo sto solo esaminando per primo. Ho sempre e solo l'impressione di usarlo in un gestore di eventi paint, quindi forse perché non ho notato un problema prima – musefan

0

Un modo più corretto:

private static byte[] Resize(byte[] imageBytes, int width, int height) 
    { 
     using (var imagestream = new MemoryStream(imageBytes)) 
     { 
      using (var img = Image.FromStream(imagestream)) 
      { 
       using (var outStream = new MemoryStream()) 
       { 
        double y = img.Height; 
        double x = img.Width; 

        double factor = 1; 
        if (width > 0) 
         factor = width/x; 
        else if (height > 0) 
         factor = height/y; 

        using (var imgOut = new Bitmap((int)(x * factor), (int)(y * factor))) 
        { 
         using (var g = Graphics.FromImage(imgOut)) 
         { 
          g.Clear(Color.White); 
          g.DrawImage(img, new Rectangle(0, 0, (int)(factor * x), 
                (int)(factor * y)), 
           new Rectangle(0, 0, (int)x, (int)y), GraphicsUnit.Pixel); 
         } 

         imgOut.Save(outStream, ImageFormat.Jpeg); 
        } 

        return outStream.ToArray(); 
       } 
      } 
     } 

} 

È inoltre necessario essere molto attenti quando allocare e deallocare oggetti di grandi dimensioni (quelle> = 85000 byte) ... perché andare sul LOH (Large Object Heap), ed è possibile frammentarlo e quindi esaurire la memoria più velocemente di quanto ci si aspetti (ci sono varie tecniche per aggirare il problema se si incontra questo problema).